<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>KEEP GOING!</title>
    <link>https://s2choco.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 15:29:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>[Ellie]</managingEditor>
    <image>
      <title>KEEP GOING!</title>
      <url>https://tistory1.daumcdn.net/tistory/2931310/attach/b4d5183e4e8445e5b5287b37456d2046</url>
      <link>https://s2choco.tistory.com</link>
    </image>
    <item>
      <title>[GitHub] SSH 방식으로 통신하기</title>
      <link>https://s2choco.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;평소 GitHub을 사용할 때 아무 생각 없이 HTTPS로 remote를 설정하거나 clone 받아서 사용하고 있었는데, 최근 GitHub에서 보안을 강화하겠다고 password 인증 방식을 없애버렸고, 그 에러를 해결하면서 SSH 인증 방식에 대해 알게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이번 포스팅에서는 이 과정에서 알게된 SSH 방식으로 통신하도록 설정하는 방법을 정리하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&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;span style=&quot;color: #333333;&quot;&gt;로컬 Git을 GitHub과 연결하기 위해서는 어떤 방식으로 원격 저장소와 통신할 것인지를 설정해 주어야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 방식으로는 현재 아래 3가지가 있다.&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;&lt;span style=&quot;color: #333333;&quot;&gt;HTTPS 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SSH 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;GitHub CLI 방식&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; width=&quot;415&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;204&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWOiHO/btrb7bKyPMW/Kjlmm0OJJqzwnROjPQ3MoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWOiHO/btrb7bKyPMW/Kjlmm0OJJqzwnROjPQ3MoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWOiHO/btrb7bKyPMW/Kjlmm0OJJqzwnROjPQ3MoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWOiHO%2Fbtrb7bKyPMW%2FKjlmm0OJJqzwnROjPQ3MoK%2Fimg.png&quot; width=&quot;415&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;204&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※&amp;nbsp;기존에는 1, 2번 방식만 있었는데 마이크로소프트가 GitHub을 인수하면서 GitHub CLI 방식이 추가 되었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;GitHub CLI는 GitHub의 기능들(PR, 이슈, Repository 등)을 커맨드 라인으로 쓸 수 있도록 도와주는 CLI 도구이다.&lt;/span&gt;&lt;/p&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: #333333;&quot;&gt;여기서 통신 방법을 설정할 수 있는 케이스는 아래 3가지 정도로 요약할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; 1. 처음 프로젝트를 만들어서 원격 저장소에 올리려고 하는 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;code&gt;$ git remote add origin [URL]&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; 2. 원격 저장소에 있는 코드를 로컬로 받아오려는 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;code&gt;$ git clone [URL]&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; 3. 통신 방식을 바꾸려는 경우 (HTTPS &amp;rarr; SSH)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;code&gt;$ git remote set-url origin [URL]&lt;/code&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위에서 &lt;code&gt;[URL]&lt;/code&gt;에&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 들어가는 url에 따라 통신 방식이 결정된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1628961203206&quot; class=&quot;bash&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background-color: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# HTTPS 방식
&amp;gt; https://github.com/USERNAME/REPOSITORY.git

# SSH 방식
&amp;gt; git@github.com:USERNAME/REPOSITORY.git&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: #333333;&quot;&gt;현재 자신의 프로젝트는 어떤 방식으로 통신하는지 확인하기 위해서는 아래 명령어를 실행 해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1628961231612&quot; class=&quot;bash&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background-color: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote -v&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: #333333;&quot;&gt;이 결과가 &lt;code&gt;https://github.com/~~~&lt;/code&gt; 이라면 HTTPS 방식으로, &lt;code&gt;git@github.com:~~~&lt;/code&gt; 이라면 SSH 방식으로 통신하고 있는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 본론으로 ...&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;SSH(=Secure Shell)는 네트워크 프로토콜로, SSH를 사용해 안전하게 네트워크 통신을 할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SSH 프로토콜은 사용자 인증 방식을 필요로 하는데, GitHub에서는 공개 키(Public Key) 인증 방식을 사용한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;공개 키 인증 방식은&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;사용자 컴퓨터에 등록 되어 있는 공개 키(Public Key)-개인 키(Private Key) 쌍&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;과&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;GitHub 서버에 등록 되어 있는 공개 키&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;를 비교해 올바른 사용자인지 인증하는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;GitHub에 공개 키가 등록 되어있는지 확인하는 가장 간단한 방법은 아무 리포지토리에 들어가서 clone을 클릭해서 SSH 탭을 들어갔을 때 SSH Key가 없는 경우 아래 노란색 박스 경고창이 뜬다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;419&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;580&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxBLa4/btrb6SYKTDQ/htCuxa0QkeNkOhuunxMFJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxBLa4/btrb6SYKTDQ/htCuxa0QkeNkOhuunxMFJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxBLa4/btrb6SYKTDQ/htCuxa0QkeNkOhuunxMFJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxBLa4%2Fbtrb6SYKTDQ%2FhtCuxa0QkeNkOhuunxMFJ1%2Fimg.png&quot; width=&quot;419&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;580&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;경고창이 없으면 이미 키가 등록 되어 있는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※&amp;nbsp;&lt;/span&gt;만약 키가 있는데 컴퓨터의 키와 다른 경우, 네트워크 통신 시도 시 &lt;code&gt;ssh permission denied (publickey)&lt;/code&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;에러가 뜬다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 때는 컴퓨터의 키로 GitHub에 등록된 키를 업데이트 해 주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;키가 등록되어 있지 않은 경우, 일단 컴퓨터에 이미 SSH 키 쌍이 존재하는지 확인해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;컴퓨터에 이미 SSH 키 쌍이 존재한다면 그 중 공개 키를 GitHub에 등록하면 되고, 없다면 먼저 키 쌍을 생성해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;컴퓨터에서 SSH 키 존재 여부 확인&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;일반적으로 사용자 키 파일은&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt; &lt;code&gt;~/.ssh&lt;/code&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;&amp;nbsp;경로에 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;키를 확인하기 위해 터미널에 아래 명령어를 실행해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1628961306311&quot; class=&quot;bash&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background-color: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ls -al ~/.ssh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; width=&quot;522&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;156&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKkzmi/btrb7zYDesw/A1JlHTVRMHa0gkhV6qZ8WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKkzmi/btrb7zYDesw/A1JlHTVRMHa0gkhV6qZ8WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKkzmi/btrb7zYDesw/A1JlHTVRMHa0gkhV6qZ8WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKkzmi%2Fbtrb7zYDesw%2FA1JlHTVRMHa0gkhV6qZ8WK%2Fimg.png&quot; width=&quot;522&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;156&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;키 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 아무것도 없으면 키를 생성해 주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;키는 ed25519 방식으로 생성할 수 있고, rsa 방식으로 생성할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1628961306311&quot; class=&quot;bash&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background-color: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ed25519 
$ ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;

# rsa 
$ ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; width=&quot;558&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;214&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccfsmA/btrb7yZKM5f/SLZtzQiCQktM8W2OlxhxR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccfsmA/btrb7yZKM5f/SLZtzQiCQktM8W2OlxhxR1/img.png&quot; data-alt=&quot;ed25519 방식 키 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccfsmA/btrb7yZKM5f/SLZtzQiCQktM8W2OlxhxR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccfsmA%2Fbtrb7yZKM5f%2FSLZtzQiCQktM8W2OlxhxR1%2Fimg.png&quot; width=&quot;558&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;214&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;ed25519 방식 키 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; width=&quot;553&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;204&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be5kGj/btrcb86Q5cO/qIsYdzfqxqMYUzD1s9C110/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be5kGj/btrcb86Q5cO/qIsYdzfqxqMYUzD1s9C110/img.png&quot; data-alt=&quot;rsa 방식 키 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be5kGj/btrcb86Q5cO/qIsYdzfqxqMYUzD1s9C110/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe5kGj%2Fbtrcb86Q5cO%2FqIsYdzfqxqMYUzD1s9C110%2Fimg.png&quot; width=&quot;553&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;204&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;rsa 방식 키 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 .pub로 끝나는 파일이 공개 키 이고 없는 파일이 개인 키이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;키 조회&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;cat 명령어로 공개 키를 조회해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1628961306311&quot; class=&quot;bash&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background-color: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ed25519 
$ cat ~/.ssh/id_ed25519.pub 

# rsa 
$ cat ~/.ssh/id_rsa.pub&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: #333333;&quot;&gt;조회한 값이 ssh-ed25519 또는 ssh-rsa로 시작하면 올바른 공개 키라고 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;발급 받은 키 GitHub에 등록&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로필을 클릭해서 [Settings]에 들어간다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; width=&quot;193&quot; height=&quot;432&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;922&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nWg7M/btrb7bKzAQW/qNulafTAIQMw5kkCT9cKEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nWg7M/btrb7bKzAQW/qNulafTAIQMw5kkCT9cKEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nWg7M/btrb7bKzAQW/qNulafTAIQMw5kkCT9cKEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnWg7M%2Fbtrb7bKzAQW%2FqNulafTAIQMw5kkCT9cKEk%2Fimg.png&quot; width=&quot;193&quot; height=&quot;432&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;922&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. [SSH and GPG keys] 탭에 들어가서 [New SSH key]를 클릭한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; width=&quot;777&quot; height=&quot;381&quot; data-origin-width=&quot;1978&quot; data-origin-height=&quot;970&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/do1lqa/btrcc6gFZjI/ek8TW0piYdibMEYJgaX7m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/do1lqa/btrcc6gFZjI/ek8TW0piYdibMEYJgaX7m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/do1lqa/btrcc6gFZjI/ek8TW0piYdibMEYJgaX7m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdo1lqa%2Fbtrcc6gFZjI%2Fek8TW0piYdibMEYJgaX7m1%2Fimg.png&quot; width=&quot;777&quot; height=&quot;381&quot; data-origin-width=&quot;1978&quot; data-origin-height=&quot;970&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. Key 탭에 위에서 조회한 키를 넣고 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;840&quot; data-filename=&quot;스크린샷 2021-08-15 오후 2.13.25.png&quot; width=&quot;595&quot; height=&quot;332&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Tbbk/btrcgO1gSdr/3GPCvfYS01Q3Lr3TO2oO60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Tbbk/btrcgO1gSdr/3GPCvfYS01Q3Lr3TO2oO60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Tbbk/btrcgO1gSdr/3GPCvfYS01Q3Lr3TO2oO60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Tbbk%2FbtrcgO1gSdr%2F3GPCvfYS01Q3Lr3TO2oO60%2Fimg.png&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;840&quot; data-filename=&quot;스크린샷 2021-08-15 오후 2.13.25.png&quot; width=&quot;595&quot; height=&quot;332&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;끝!&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다시 아무 리포지토리 Clone에 들어가 보면 노란 경고창이 사라진 것으로 GitHub에 공개 키가 등록 되었음을 간접적으로 확인할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 SSH URL을 통해 GitHub과 통신할 수 있다!  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;415&quot; height=&quot;151&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;270&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxeG6H/btrb7bwY7lA/2DZzQ3GJowOjCKDWMWAjC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxeG6H/btrb7bwY7lA/2DZzQ3GJowOjCKDWMWAjC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxeG6H/btrb7bwY7lA/2DZzQ3GJowOjCKDWMWAjC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxeG6H%2Fbtrb7bwY7lA%2F2DZzQ3GJowOjCKDWMWAjC0%2Fimg.png&quot; width=&quot;415&quot; height=&quot;151&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;270&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>GitHub</category>
      <category>ssh</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/46</guid>
      <comments>https://s2choco.tistory.com/46#entry46comment</comments>
      <pubDate>Sun, 15 Aug 2021 08:35:45 +0900</pubDate>
    </item>
    <item>
      <title>[GitHub] personal access token으로 인증하기 (for MacOS)</title>
      <link>https://s2choco.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;오늘도 여느 때와 같이 터미널에서 Github으로 push를 하려고 하는데 갑자기 아래와 같은 에러가 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2070&quot; data-origin-height=&quot;172&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmHQXV/btrb6UhJ8wj/PmrFKNM2HFl1KDITwZU6s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmHQXV/btrb6UhJ8wj/PmrFKNM2HFl1KDITwZU6s0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmHQXV/btrb6UhJ8wj/PmrFKNM2HFl1KDITwZU6s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmHQXV%2Fbtrb6UhJ8wj%2FPmrFKNM2HFl1KDITwZU6s0%2Fimg.png&quot; data-origin-width=&quot;2070&quot; data-origin-height=&quot;172&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에러 메시지를 확인해 보니 어제(8/13)부로 Https 통신할 때 사용하던 password 인증 방식을 지원하지 않는다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;즉, 이제는 무조건 personal access token을 사용해 인증해야 한다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 토큰을 이용해 추가 인증을 하는 방식을 2FA(Two-Factor Authentication)라고 하는데, GitHub에서 이를 통해 보안을 강화하려고 하는 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;해결 방법&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. personal access token 발급 받기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;토큰 생성 방법은 &lt;a href=&quot;https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-token&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에 사진과 함께 자세한 설명이 나와 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. password 값을 &amp;nbsp;발급 받은 토큰으로 업데이트 (in MacOS)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;MacOS는 GitHub에서 사용하는 username, password 등의 credential 정보를 컴퓨터에 캐싱해 두고 사용하기 때문에 이 캐싱된 값을 변경해 주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;1084&quot; width=&quot;611&quot; height=&quot;418&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWzGr7/btrb6Uozykw/kDE8zl4Vmx13gKR83OVnz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWzGr7/btrb6Uozykw/kDE8zl4Vmx13gKR83OVnz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWzGr7/btrb6Uozykw/kDE8zl4Vmx13gKR83OVnz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWzGr7%2Fbtrb6Uozykw%2FkDE8zl4Vmx13gKR83OVnz1%2Fimg.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;1084&quot; width=&quot;611&quot; height=&quot;418&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;먼저, keychain access 앱을 열어서 &quot;github.com&quot;으로 검색한 두 [종류]가 '인터넷 암호'로 되어 있는 아이템을 찾는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;660&quot; data-filename=&quot;스크린샷 2021-08-14 오후 5.40.40.png&quot; width=&quot;623&quot; height=&quot;281&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/opzp5/btrb7yywqgD/JcO7GuP3IlaDORCWFgs3l0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/opzp5/btrb7yywqgD/JcO7GuP3IlaDORCWFgs3l0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/opzp5/btrb7yywqgD/JcO7GuP3IlaDORCWFgs3l0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fopzp5%2Fbtrb7yywqgD%2FJcO7GuP3IlaDORCWFgs3l0%2Fimg.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;660&quot; data-filename=&quot;스크린샷 2021-08-14 오후 5.40.40.png&quot; width=&quot;623&quot; height=&quot;281&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;해당 아이템에 들어가서 [암호 보기]를 체크한 뒤 발급 받은 토큰을 입력하고 변경사항을 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;698&quot; data-filename=&quot;스크린샷 2021-08-14 오후 5.50.24.png&quot; width=&quot;509&quot; height=&quot;336&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IiWAF/btrb7tKCkUv/77beqT1tipeDmGy3W6OlwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IiWAF/btrb7tKCkUv/77beqT1tipeDmGy3W6OlwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IiWAF/btrb7tKCkUv/77beqT1tipeDmGy3W6OlwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIiWAF%2Fbtrb7tKCkUv%2F77beqT1tipeDmGy3W6OlwK%2Fimg.png&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;698&quot; data-filename=&quot;스크린샷 2021-08-14 오후 5.50.24.png&quot; width=&quot;509&quot; height=&quot;336&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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;성공!&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;426&quot; data-filename=&quot;스크린샷 2021-08-14 오후 6.15.01.png&quot; width=&quot;565&quot; height=&quot;178&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmOLs8/btrb7bDy2xY/OpcOQJqYk1Veif2iTQaoz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmOLs8/btrb7bDy2xY/OpcOQJqYk1Veif2iTQaoz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmOLs8/btrb7bDy2xY/OpcOQJqYk1Veif2iTQaoz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmOLs8%2Fbtrb7bDy2xY%2FOpcOQJqYk1Veif2iTQaoz0%2Fimg.png&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;426&quot; data-filename=&quot;스크린샷 2021-08-14 오후 6.15.01.png&quot; width=&quot;565&quot; height=&quot;178&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;/figure&gt;
&lt;/p&gt;</description>
      <category>Programming</category>
      <category>GitHub</category>
      <category>personal access token</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/45</guid>
      <comments>https://s2choco.tistory.com/45#entry45comment</comments>
      <pubDate>Sat, 14 Aug 2021 16:34:47 +0900</pubDate>
    </item>
    <item>
      <title>[Android] ExoPlayer blink(깜빡임 현상) 제거하기</title>
      <link>https://s2choco.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/google/ExoPlayer&quot;&gt;ExoPlayer&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;는 구글에서 만든 동영상 재생 라이브러리이다. MediaPlayer보다 훨씬 안정적이고 다양한 기능을 지원하기 때문에 유튜브나 인스타그램 등 많은 곳에서 ExoPlayer를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그래서 진행중인 프로젝트에서&lt;/span&gt; &lt;span style=&quot;color: #0593d3;&quot;&gt;ExoPlayer를 사용해 동영상이 준비되기 전에는 썸네일 이미지를 보여주고, 준비가 완료되면 자연스럽게 동영상을 재생하려고 했는데 재생 직전 검은 화면으로 깜빡이는 현상을 확인했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이번 포스팅에서는 이 문제의 원인과 해결 방법을 공유하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&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;span style=&quot;color: #333333;&quot;&gt;ExoPlayer는 동영상을 재생하는 Player가 있고, 화면에 보여주는 PlayerView에 Player를 등록해서 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그런데 PlayerView는 동영상을 재생하는 View이기 때문에 background 속성이 없고, 썸네일을 위한 기능은 없는 것 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;artwork라는 속성으로 PlayerView의 백그라운드를 drawable로 지정할 수는 있지만 Bimap 이미지로는 설정할 수 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그렇기 때문에 동영상을 재생하기 위한 PlayerView와 크기가 동일한 ImageView를 위에 두고 PlayerView가 준비되기 전에는 ImageView로 썸네일 이미지를 보여주고 동영상 재생 준비가 되었을 때 ImageView를 invisible 시켜 PlayerView를 보여주는 방식을 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그런데, Player에서 재생 준비가 된 시점에서 PlayerView를 보여주면 잠시 검은 화면이 보이면서 깜빡이는 현상이 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;artwork를 지정하면 검은 화면이 아닌 지정한 artwork가 나온다. 그래서 artwork를 투명한 색상으로 설정해 봤지만 여전히 검은 화면이 나왔다.. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 문제를 해결하기 위해 여러 삽질을 했지만 결국 이 문제의 궁극적인 원인은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;동영상을 재생할 때 첫 프레임이 그려지기 전까지 잠깐 동안 검은 화면이 보이게 되는 것&lt;/span&gt;이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이를 위해 ExoPlayer에서는 첫 프레임이 그려진 뒤 호출되는 콜백 함수가 있었고, 이를 이용해 첫 프레임이 그려진 뒤에 ImageView를 invisible 시켜&amp;nbsp;PlayerView를 보여주는 것으로 해결했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1621427179303&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;simpleExoPlayer.addVideoListener(object : VideoListener {
    override fun onRenderedFirstFrame() {
        super.onRenderedFirstFrame()
        
        // 첫 프레임이 그려진 뒤에 썸네일 이미지뷰를 없앤다. (재생 전 깜빡이는 현상 방지)
        binding.thumbnailView.visibility = View.GONE
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Mobile/Android</category>
      <category>Android</category>
      <category>blink</category>
      <category>ExoPlayer</category>
      <category>깜빡임</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/41</guid>
      <comments>https://s2choco.tistory.com/41#entry41comment</comments>
      <pubDate>Wed, 19 May 2021 21:34:04 +0900</pubDate>
    </item>
    <item>
      <title>Android Studio 자주 쓰는 단축키 모음</title>
      <link>https://s2choco.tistory.com/42</link>
      <description>&lt;h2&gt;&lt;span style=&quot;color: #000000;&quot;&gt;For Windows&lt;/span&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;&lt;b&gt;단축키&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 73.0233%;&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;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Shift + F6&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;동일하게 연결 되어 있는 변수명 변경&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Shift 2번&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;프로젝트 내 클래스/파일/심볼/액션 찾기&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + F4&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;탭 닫기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + B&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + Click&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;선언부로 이동&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ctrl + Alt + B&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구현부로 이동&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ctrl + Shift + I&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선언부 팝업창으로 보기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + F&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;현재 파일에서 찾기 (Find)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + Shift + F&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;전체 파일에서 찾기 (Find)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + R&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;현재 파일에서 바꾸기 (Replace)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + Shift + R&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;전체 파일에서 바꾸기 (Replace)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + D&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;라인 or 블럭 복제 (Duplication)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Alt + 드래그&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;세로로 블럭 지정해서 한 번에 수정하기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + H&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;클래스 계층(Hierarchy) 보기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + I&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Implement methods 구현&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + O&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Override mothods 구현&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + Alt + I&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;자동 indentation&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + Alt&amp;nbsp;+ O&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;사용하지 않는 import 제거&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Alt + F7&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;사용처 찾기 (프로젝트 계층 단위로 볼 수 있음)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ctrl + Alt&lt;/span&gt; + 위/아래&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용처 찾기 모드에서 위/아래 사용처로 이동&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + Alt + 좌/우&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이전/이후 커서 위치로 이동 (with 파일 간 이동)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ctrl + &quot;+/-&quot;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 스코프 펴기/접기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ctrl + Shift + &quot;+/-&quot;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파일 전체 펴기/접기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ctrl + Shift&lt;/span&gt;&amp;nbsp;+ T&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 코드 만들기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Shift + Delete&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + X&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;해당 라인 잘라내기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Alt + 숫자키(1~9)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 패널 열고 닫기 (&lt;span style=&quot;color: #ee2323;&quot;&gt;Alt + 7 : Code Structure&lt;/span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 26.8605%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Alt + 위/아래&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 73.0233%;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;method 간 이동&lt;/span&gt;&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;&lt;span style=&quot;color: #000000;&quot;&gt;+ 단말 내부 파일 구조 확인: View &amp;gt; Tool Windows &amp;gt; Device File Explorer&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;span style=&quot;color: #000000;&quot;&gt;For Mac&lt;/span&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;&lt;b&gt;단축키&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 610px;&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;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;shift + F6&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;동일하게 연결 되어 있는 변수명 변경&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;shift 2번&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;프로젝트 내 클래스/파일/심볼/액션 찾기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Ctrl + F4&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;탭 닫기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command + B&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command&amp;nbsp;+ Click&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;선언부로 이동&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;command + option + B&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구현부로 이동&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;option + space&lt;br /&gt;command + Y&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선언부 팝업창으로 보기&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command&amp;nbsp;+ F&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;현재 파일에서 찾기 (Find)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command + shift + F&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;전체 파일에서 찾기 (Find)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command&amp;nbsp;+ R&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;현재 파일에서 바꾸기 (Replace)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command + shift + R&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;전체 파일에서 바꾸기 (Replace)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command&amp;nbsp;+ D&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;라인 or 블럭 복제 (Duplication)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;option + 드래그&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;세로로 블럭 지정해서 한 번에 수정하기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;control&amp;nbsp;+ H&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;클래스 계층(Hierarchy) 보기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;control&amp;nbsp;+ I&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Implement methods 구현&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;control&amp;nbsp;+ O&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Override mothods 구현&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;control + option + I&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;자동 indentation&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;control + option&amp;nbsp;+ O&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;사용하지 않는 import 제거&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command + option + 좌/우&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이전/이후 커서 위치로 이동 (with 파일 간 이동)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;command + shift + &quot;+/-&quot;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파일 전체 펴기/접기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;command + shift&lt;/span&gt;&amp;nbsp;+ T&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 코드 만들기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command + delete&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;해당 라인 지우기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;command + X&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;해당 라인 잘라내기&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 215px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;command + 숫자키(1~9)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; width: 610px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 패널 열고 닫기&lt;/span&gt;&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;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;&lt;span style=&quot;color: #000000;&quot;&gt;관련 링크&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;- &lt;a href=&quot;https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf&quot;&gt;https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf&lt;/a&gt;&lt;/p&gt;</description>
      <category>끄적끄적</category>
      <category>AndroidStudio</category>
      <category>Mac</category>
      <category>shortcuts</category>
      <category>Windows</category>
      <category>단축키</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/42</guid>
      <comments>https://s2choco.tistory.com/42#entry42comment</comments>
      <pubDate>Fri, 11 Sep 2020 17:57:12 +0900</pubDate>
    </item>
    <item>
      <title>Android Studio 내부 소스 코드 보는 방법</title>
      <link>https://s2choco.tistory.com/44</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.28.32.png&quot; data-origin-width=&quot;2102&quot; data-origin-height=&quot;1106&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G7f0W/btqFNrjYIS6/GCwnS6Z6yHe8vXLc3eWfjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G7f0W/btqFNrjYIS6/GCwnS6Z6yHe8vXLc3eWfjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G7f0W/btqFNrjYIS6/GCwnS6Z6yHe8vXLc3eWfjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG7f0W%2FbtqFNrjYIS6%2FGCwnS6Z6yHe8vXLc3eWfjK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.28.32.png&quot; data-origin-width=&quot;2102&quot; data-origin-height=&quot;1106&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;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;안드로이드 스튜디오에서 내부 소스 코드를 보려고 할 때 위 처럼 모든 구현부가 &lt;span style=&quot;color: #333333;&quot;&gt;RuntimeException&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;을 던지는 파일이 나올 때가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1595053308573&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;throw new RuntimeException(&quot;Stub!&quot;);&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;메시지 내용은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Decompiled .class file, bytecode version: 52.0 (Java 8)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Sources for 'Android API 30 Platform' not found.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 문제는 &lt;span style=&quot;color: #0593d3;&quot;&gt;안드로이드 API 버전에 해당하는 Source가 다운로드 되어 있지 않아서 발생&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;여기서 두 번째 &lt;b&gt;&quot;Sources for 'Android API 30 Platform' not found.&quot;&lt;/b&gt; 메시지는 안드로이드 API 30 버전의 Source가 없으니 다운로드를 하라는 메시지이다. 하지만 현재 30 버전의 소스코드는 공개되지 않은 상태이기 때문에 다운로드 받을 수 없다.&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: #333333;&quot;&gt;이를 위해 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Android 버전을 29로 내려야 한다&lt;/span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 방법은 앱 모듈의 gradle 파일 - &lt;span style=&quot;color: #0593d3;&quot;&gt;build.gradle(Module: app)&lt;/span&gt; - 의 compileSdkVersion을 29로 설정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&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;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.09.41.png&quot; data-origin-width=&quot;1510&quot; data-origin-height=&quot;290&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btKqN2/btqFNPq8oyw/hbQ6vmEBb5GmS1hsGq4wK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btKqN2/btqFNPq8oyw/hbQ6vmEBb5GmS1hsGq4wK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btKqN2/btqFNPq8oyw/hbQ6vmEBb5GmS1hsGq4wK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtKqN2%2FbtqFNPq8oyw%2FhbQ6vmEBb5GmS1hsGq4wK0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.09.41.png&quot; data-origin-width=&quot;1510&quot; data-origin-height=&quot;290&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;&lt;span style=&quot;color: #333333;&quot;&gt;이는 targetSdkVersion보다 compileSdkVersion이 낮을 수 없기 때문인데, 이는 Gradle 호환성과 관련이 있다. 해결 방법은 2가지가 있다.&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;첫 번째 방법은 targetSdkVersion을 맞춘다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1595055190224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;android {
    compileSdkVersion 29
    ...
    defaultConfig {
        ...
        targetSdkVersion 29
        ...
    }

    ...
}&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;두 번째 방법은 주석을 추가해서 Gradle 호환성 문제를 검사하지 말라고 컴파일러에게 알려준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1595055201372&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;android {
    // noinspection GradleCompatible
    compileSdkVersion 29
    
    ...

}&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;span style=&quot;color: #333333;&quot;&gt;이제 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;API 29에 해당하는 Source가 있는지 확인&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;안드로이드 스튜디오에 [Preferences] &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(Windows에서는 [Settings])&lt;/span&gt; 에서 [Android Sdk]로 들어간다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래 체크박스에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;show Package Details 체크&lt;/span&gt;해서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Sources for Android 29가 Installed 되었는지 확인&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-origin-width=&quot;1390&quot; data-origin-height=&quot;128&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.06.06.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKarVm/btqFM949qnW/CpTevS5bxsewh5kpahDGr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKarVm/btqFM949qnW/CpTevS5bxsewh5kpahDGr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKarVm/btqFM949qnW/CpTevS5bxsewh5kpahDGr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKarVm%2FbtqFM949qnW%2FCpTevS5bxsewh5kpahDGr1%2Fimg.png&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;128&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.06.06.png&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;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 하면 API 29에 대한 내부 소스 코드를 볼 수 있다!&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-07-18 오후 3.08.06.png&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;840&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vIfvp/btqFNEpSKNZ/bArQcqQcze4uAsREsfUGlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vIfvp/btqFNEpSKNZ/bArQcqQcze4uAsREsfUGlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vIfvp/btqFNEpSKNZ/bArQcqQcze4uAsREsfUGlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvIfvp%2FbtqFNEpSKNZ%2FbArQcqQcze4uAsREsfUGlk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 3.08.06.png&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;840&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;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>끄적끄적</category>
      <category>AndroidStudio</category>
      <category>RuntimeException</category>
      <category>소스코드</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/44</guid>
      <comments>https://s2choco.tistory.com/44#entry44comment</comments>
      <pubDate>Sat, 18 Jul 2020 16:06:41 +0900</pubDate>
    </item>
    <item>
      <title>[Android] ListAdapter로 리스트 효율적으로 관리하기 (with DiffUtil)</title>
      <link>https://s2choco.tistory.com/33</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이전 포스팅에서&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://s2choco.tistory.com/26&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RecyclerView의 아이템을 LiveData로 관리하는 방법&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에 대해 알아보았다. 이 때 함께 사용하면 좋은 ListAdapter에 대해 소개하려고 한다.&lt;/span&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;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;RecyclerView를 사용하다보면 아이템을 변경할 일이 많고, 이럴 때 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기존에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;notifyDataSetChanged()&lt;/span&gt;로 모든 아이템을 업데이트 하는 방법을 사용해 왔다. 하지만 이 방법은 아이템 개수가 많아질수록 비효율적일 수 밖에 없다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;물론 아래 notify- 메서드를 잘 이용하면 전체 데이터를 바꿀 필요가 없지만 position을 가지고 수동으로 관리해 주어야 하는 불편함이 있을 수 있고,&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;아이템의 정해진 순서가 없을 때는 이 기능을 사용할 수 없다.&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; width=&quot;667&quot; height=&quot;NaN&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;362&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/etGeOK/btqELRxEcYH/trPVq3JEcwWv4n5y1jie21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/etGeOK/btqELRxEcYH/trPVq3JEcwWv4n5y1jie21/img.png&quot; data-alt=&quot;RecyclerView 아이템의 변경사항을 notify하는 메서드들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/etGeOK/btqELRxEcYH/trPVq3JEcwWv4n5y1jie21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FetGeOK%2FbtqELRxEcYH%2FtrPVq3JEcwWv4n5y1jie21%2Fimg.png&quot; width=&quot;667&quot; height=&quot;NaN&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;362&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;RecyclerView 아이템의 변경사항을 notify하는 메서드들&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;이런 문제를 알고 구글에서는 DiffUtil이라는 매우 편리한 유틸리티 클래스를 만들었는데, 이 클래스는 두 리스트의 차이점을 찾아 업데이트 되어야 할 목록을 반환 해 줘서 RecyclerView 어댑터에 대한 업데이트를 알리는데 사용된다.&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;blockquote data-ke-style=&quot;style2&quot;&gt;DiffUtil 사용하기&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;DiffUtil을 사용하는 방법은 다음과 같다. 먼저&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;DiffUtil.Callback&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;을 구현한 클래스를 만들어야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591870722197&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PersonDiffCallback(
    private val oldList: List&amp;lt;Person&amp;gt;,
    private val newList: List&amp;lt;Person&amp;gt;
) : DiffUtil.Callback() {
    override fun getOldListSize() = oldList.size

    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
        oldList[oldItemPosition].id == newList[newItemPosition].id

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
        oldList[oldItemPosition] == newList[newItemPosition]
}&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;이 Callback 클래스를 RecyclerView의 리스트 업데이트 하는 함수에 아래와 같이 코드를 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591873877820&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PersonDiffAdapter : RecyclerView.Adapter&amp;lt;PersonViewHolder&amp;gt;() {
    private val people = mutableListOf&amp;lt;Person&amp;gt;()
    
    ...
    
    fun replaceItems(newPeople: List&amp;lt;Person&amp;gt;) {
        val diffCallback = PersonDiffCallback(people, newPeople)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        
        people.clear()
        people.addAll(newPeople)
        
        diffResult.dispatchUpdatesTo(this)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;DiffUtil의 메서드의 역할은&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&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;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;calculateDiff()&lt;/span&gt;에서 diff 알고리즘을 통해 변경된 아이템을 감지하고,&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;dispatchUpdatesTo()&lt;/span&gt;에서 지정된 Adapter로 업데이트 이벤트를 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;추가로...&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;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;DiffUtil은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;dispatchUpdatesTo()&lt;/span&gt;에서 변경사항을 리스트에 적용할 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;추가 및 삭제 애니메이션&lt;/span&gt;을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;DiffUtil은 아이템 개수가 많을 경우 calculateDiff()의 diff 계산 시간이 길어질 수 있기 때문에&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;백그라운드 스레드에서 처리&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;dispatchUpdatesTo()로 RecyclerView에 변경 사항을 적용하기 전에 원본 리스트를 새로운 리스트로 교체하는 작업을 해 줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 3번에 대해 부가 설명을 하려고 한다. 이 부분은 이유를 알아보기 전에 일단 dispatchUpdatesTo()에서 하는 일을 알아야 한다. &lt;/span&gt;dispatchUpdatesTo()에서는 diff 계산에서 반환된 DiffResult 객체가 변경사항을 Adapter에 전달하고 Adapter가 변경 사항에 대해 알림을 받는다. 이 알림은 위에서 소개한 notify- 메서드로 변경 사항에 대해 리스트의 아이템이 업데이트 된다.&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;아래 코드는 DiffUtil에서 Adapter로 변경 사항을 notify 메서드로 알리는 부분이다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-06-11 오후 8.30.38.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;838&quot; width=&quot;509&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SWZye/btqENA9dCJd/kox48CipqkrVzdSzkQmIyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SWZye/btqENA9dCJd/kox48CipqkrVzdSzkQmIyK/img.png&quot; data-alt=&quot;DiffUtil에서 사용하는 notify- 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SWZye/btqENA9dCJd/kox48CipqkrVzdSzkQmIyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSWZye%2FbtqENA9dCJd%2Fkox48CipqkrVzdSzkQmIyK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-11 오후 8.30.38.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;838&quot; width=&quot;509&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;DiffUtil에서 사용하는 notify- 메서드&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;&lt;span style=&quot;color: #333333;&quot;&gt;아래 글은 DiffUtil 공식 문서에 나와 있는 dispatchUpdatesTo()에 대한 설명이다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&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-origin-width=&quot;1746&quot; data-origin-height=&quot;914&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sHEVP/btqEMw0UjBs/rcpl6rm9cAPmcIkW3Af9kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sHEVP/btqEMw0UjBs/rcpl6rm9cAPmcIkW3Af9kK/img.png&quot; data-alt=&quot;DiffUtil 공식 문서의 dispatchUpdatesTo() 설명&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sHEVP/btqEMw0UjBs/rcpl6rm9cAPmcIkW3Af9kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsHEVP%2FbtqEMw0UjBs%2Frcpl6rm9cAPmcIkW3Af9kK%2Fimg.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;914&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;DiffUtil 공식 문서의 dispatchUpdatesTo() 설명&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 style=&quot;color: #333333;&quot;&gt;리스트를 교체한 직후&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;업데이트 이벤트를 어댑터로 전송하고 그 이후에 &lt;span style=&quot;color: #333333;&quot;&gt;RecyclerView가 리스트에 접근해야 하&lt;/span&gt;기 때문에 dispatchUpdatesTo()를 호출하기 전에 리스트를 변경해야 한다고 한다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;즉, &lt;/span&gt;dispatchUpdatesTo()에서는 데이터를 변경할 때 notify- 메서드로 즉시 어댑터로 업데이트 이벤트를 전달하기 때문에 그 전에 새로운 리스트로 교체를 해야 &lt;span style=&quot;color: #333333;&quot;&gt;RecyclerView에&amp;nbsp;&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;또한 어댑터에서 사용하는 리스트는 MutableList로 만들어야 제대로 동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AsyncListDiffer 사용하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;DiffUtil 클래스를 사용하기 위해 개발자는 직접 백그라운드 스레드에서 비교 처리를 수행하고 결과를 메인 스레드에서 처리하는 코드를 작성해야 했다. 하지만 AsyncListDiffer 클래스는 이런 boiler plate 작업을 줄이기 위해 내부적으로 diff 계산을 백그라운드 스레드로 처리한 뒤 리스트 업데이트까지 해 준다. 덕분에 우리는 스레드를 신경쓰지 않고 DiffUtil을 훨씬 편하게 사용할 수 있게 되었다.&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;사용하는 방법 DiffUtil보다 훨씬 간단하다. 먼저&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: #006dd7;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;DiffUtil.ItemCallback&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;을 구현한 클래스를 만들어야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591888068807&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PersonDiffItemCallback : DiffUtil.ItemCallback&amp;lt;Person&amp;gt;() {
    override fun areItemsTheSame(oldItem: Person, newItem: Person) =
        oldItem.id == newItem.id

    override fun areContentsTheSame(oldItem: Person, newItem: Person) =
        oldItem == newItem
}&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;그리고 RecyclerView Adapter에서 AsyncListDiffer를 생성해 아래처럼 사용하기만 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591888051739&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PersonAsyncDifferAdapter : RecyclerView.Adapter&amp;lt;PersonViewHolder&amp;gt;() {
    private val asyncDiffer = AsyncListDiffer(this, PersonDiffItemCallback())

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PersonViewHolder(
        ItemPersonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    )

    override fun onBindViewHolder(holder: PersonViewHolder, position: Int) =
        holder.bind(asyncDiffer.currentList[position])

    override fun getItemCount() = asyncDiffer.currentList.size

    fun replaceItems(newPeople: List&amp;lt;Person&amp;gt;) {
        asyncDiffer.submitList(newPeople)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AsyncListDiffer에서&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;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;submitList()&lt;/span&gt;를 호출하면 diffing 작업과 리스트 변경까지 진행하고,&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;currentList&lt;/span&gt;로 현재 리스트의 아이템들을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AsyncListDiffer에서 넘어오는 currentList는&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;READ ONLY&lt;/span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;리스트로 변경이 불가능하다. 그렇기 때문에 currentList의 아이템을 변경하기 위해서는 submitList()를 통해서만 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ListAdapter 사용하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AsyncListDiffer를 내부적으로 사용하고 있는 클래스가 바로 ListAdapter 클래스이다. 우리는 이제 그저 ListAdapter를 상속한 클래스를 만든 것 만으로도 AsyncListDiffer를 사용할 수 있게 되었다.&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;ListAdapter는 추상클래스로 아래 구현부 코드를 보면 우리가 AsyncListDiffer를 사용했을 때 코드처럼 RecyclerView의 Adapter를 상속하고, AsyncListDiffer를 만들어서 사용하고 있다. 그렇기 때문에 AsyncListDiffer의 &lt;span style=&quot;color: #0593d3;&quot;&gt;submitList()&lt;/span&gt;와 &lt;span style=&quot;color: #0593d3;&quot;&gt;currentList&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-06-12 오전 12.40.55.png&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;736&quot; width=&quot;606&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NmRpO/btqENscEX0w/MlJmHIy4MtElXes1IWYbdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NmRpO/btqENscEX0w/MlJmHIy4MtElXes1IWYbdk/img.png&quot; data-alt=&quot;ListAdapter 구현부 코드 스니펫&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NmRpO/btqENscEX0w/MlJmHIy4MtElXes1IWYbdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNmRpO%2FbtqENscEX0w%2FMlJmHIy4MtElXes1IWYbdk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-12 오전 12.40.55.png&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;736&quot; width=&quot;606&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;ListAdapter 구현부 코드 스니펫&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;ListAdapter를 사용한 RecyclerView Adapter는 아래처럼 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591890586571&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PersonListAdapter : ListAdapter&amp;lt;Person, PersonViewHolder&amp;gt;(diffUtil) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PersonViewHolder(
        ItemPersonBinding.inflate(LayoutInflater.from(parent.context), parent,false)
    )

    override fun onBindViewHolder(holder: PersonViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    fun replaceItems(items: List&amp;lt;Person&amp;gt;) {
        submitList(items)
    }

    companion object {
        val diffUtil = object: DiffUtil.ItemCallback&amp;lt;Person&amp;gt;() {
            override fun areContentsTheSame(oldItem: Person, newItem: Person) =
                oldItem == newItem

            override fun areItemsTheSame(oldItem: Person, newItem: Person) =
                oldItem.name == newItem.name
        }
    }
}&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;위의 코드를 보면 기존 &lt;span style=&quot;color: #333333;&quot;&gt;RecyclerView Adapter를 상속하는&amp;nbsp;&lt;/span&gt;방식과 달라진 점을 볼 수 있다.&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;p&gt;&lt;span style=&quot;letter-spacing: 0px; color: #0593d3;&quot;&gt;getItemCount()&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;오버라이딩&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;메서드가 없다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;getItem(position)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;메서드가 생겼다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ListAdapter 내부에서 리스트 아이템을 관리하면서 getItemCount() 메서드가 재정의 되어 있고, 리스트의 아이템을 가져오는 getItem() 메서드가 생긴 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;정리를 해 보자면, ListAdapter를 사용하면 변경 된 아이템만 UI를 업데이트 할 수 있고, 이를 위한 boiler plate 코드도 엄청나게 줄여주며, 스레드 작업과 애니메이션 작업까지 알아서 다 해준다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;약간의 불편한 점이라고는 리스트가 내부적으로 READ ONLY라서 변경을 위해서는 별도의 리스트를 두고 관리해야 한다는 정도지만, 이것도 RecyclerView의 데이터를 LiveData로 관리하는 경우에는 오히려 시너지를 내기 때문에 단점이라고 보기는 힘들 것 같다.&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;결론, ListAdapter를 사용하자!&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;[참고한 사이트]&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.kmshack.kr/RecyclerView-DiffUtil%EB%A1%9C-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81%ED%95%98%EA%B8%B0/&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;RecyclerView DiffUtil로 성능 향상하기&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@jungil.han/recyclerview-%EA%B0%9C%EB%B0%9C%EC%97%90-%EB%82%A0%EA%B0%9C-%EB%8B%AC%EA%B8%B0-539e08291160&quot;&gt;Android&amp;nbsp;Jetpack:&amp;nbsp;RecyclerView&amp;nbsp;개발에&amp;nbsp;날개&amp;nbsp;달기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/ListAdapter&quot;&gt;ListAdapter 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer&quot;&gt;AsyncListDiffer 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Mobile/Android</category>
      <category>Android</category>
      <category>AsyncListDiffer</category>
      <category>DiffUtil</category>
      <category>ListAdapter</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/33</guid>
      <comments>https://s2choco.tistory.com/33#entry33comment</comments>
      <pubDate>Fri, 12 Jun 2020 01:24:39 +0900</pubDate>
    </item>
    <item>
      <title>[Android] Context 제대로 알고 사용하자!</title>
      <link>https://s2choco.tistory.com/10</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;안드로이드에서 Context는 가장 많이 등장하고, 그만큼 핵심이 되는 개념이기 때문에 제대로 알고 사용하는 것이 좋다. 이번 포스팅에서는 Context의 역할 및 종류와 언제 사용하면 좋은지 알아 보려고 한다.&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: #000000;&quot;&gt;&lt;b&gt;Context란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Context는 &lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;어플리케이션 환경에 관한 전체 정보를 받을 수 있는 추상 클래스&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;로 액티비티, 브로드캐스트, 서비스를 시작할 때도 사용되고 리소스에 접근할 때도 Context가 사용된다. 이처럼 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;어플리케이션과 관련된 정보에 접근&lt;/span&gt;하고자 하거나 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;어플리케이션과 연관된 시스템 레벨의 함수를 호출&lt;/span&gt;하고자 할 때 Context가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;안드로이드는 어플리케이션 관련 정보를 ActivityManagerService에서 관리하고 있고, 어플리케이션과 관련된 정보에 접근하려고 할 때 해당 어플리케이션에 대한 식별자가 필요한데 그 역할을 하는 것도 Context이다.&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: #000000;&quot;&gt;&lt;b&gt;ContextWrapper와 ContextImpl&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Context는 추상 클래스이기 때문에 이를 사용하기 위해서는 구현체가 있어야 한다. 기본 구현체는 ContextImpl이고, 이 구현체는 사용자에게 노출하지 않고 ContextWrapper로 감싸져 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;[참고 사항]&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ContextImpl 클래스는 앱에서 직접 사용할 수 있는 클래스가 아니기 때문에 &lt;a href=&quot;https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;소스코드&lt;/a&gt;로만 확인할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;앞으로 &quot;Context 구현체&quot;라는 용어가 등장하는데, 이는 ContextWrapper가 wrapping하는 Context 구현체가 ContextImpl이 아닐 수 있기 때문에 ContextImpl과 용어를 구분하기 위함이다. &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: #333333;&quot;&gt;Context의 클래스 구조는 아래 그림과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-06-10 오후 5.29.17.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;918&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz5Cx5/btqEJhXVs0J/IuGCHfnID6Tpfkps3nsQP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz5Cx5/btqEJhXVs0J/IuGCHfnID6Tpfkps3nsQP0/img.png&quot; data-alt=&quot;Context 클래스 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz5Cx5/btqEJhXVs0J/IuGCHfnID6Tpfkps3nsQP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz5Cx5%2FbtqEJhXVs0J%2FIuGCHfnID6Tpfkps3nsQP0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-10 오후 5.29.17.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;918&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;Context 클래스 구조&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;ContextWrapper 클래스는 생성자로 Context 구현체를 받아서 Context 동작을 Context 구현체의 동작으로 위임한다. 즉, ContextWrapper를 상속하는 Activity, Service, Application과 같은 컴포넌트에서 ContextWrapper의 함수를 호출하면 Context 구현체의 동작을 수행하게 하는 것이다. 이를 Proxy 패턴이라고 하는데, 이 패턴을 사용하면 Context 구현체와 이를 사용하는 쪽의 결합도가 낮아져 쉽게 Context 구현체를 변경할 수 있다는 장점이 있다. 이를&lt;/span&gt; &lt;a href=&quot;https://developer.android.com/reference/android/content/ContextWrapper&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에서는 원본 Context의 변경 없이 Context 동작을 수정할 수 있다고 설명하고 있다.&amp;nbsp;&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;더 자세한 이해를 위해 ContextWrapper 클래스의 코드를 보면 아래와 같다.&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-06-10 오후 1.45.32.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;690&quot; width=&quot;615&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dx1oD0/btqEKe64Rv2/lij95BpZsB9eIQkwfTm1t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dx1oD0/btqEKe64Rv2/lij95BpZsB9eIQkwfTm1t1/img.png&quot; data-alt=&quot;ContextWrapper 코드 스니펫&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dx1oD0/btqEKe64Rv2/lij95BpZsB9eIQkwfTm1t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdx1oD0%2FbtqEKe64Rv2%2Flij95BpZsB9eIQkwfTm1t1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-10 오후 1.45.32.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;690&quot; width=&quot;615&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;ContextWrapper 코드 스니펫&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-06-10 오후 1.59.27.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;852&quot; width=&quot;609&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KQ15X/btqEIUuIc5J/jsiWcPr0QQZGzCfCzZFHdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KQ15X/btqEIUuIc5J/jsiWcPr0QQZGzCfCzZFHdk/img.png&quot; data-alt=&quot;ContextWrapper 코드 스니펫 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KQ15X/btqEIUuIc5J/jsiWcPr0QQZGzCfCzZFHdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKQ15X%2FbtqEIUuIc5J%2FjsiWcPr0QQZGzCfCzZFHdk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-10 오후 1.59.27.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;852&quot; width=&quot;609&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;ContextWrapper 코드 스니펫 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;코드에서 볼 수 있듯이 ContextWrapper는 생성자로 Context의 구현체를 받는다. 그리고 Context의 모든 동작을 Context 구현체의 함수로 위임한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 구조가 좋은 점은 앞서 언급한 것 처럼 Context 구현체를 바꾸어도 Context를 사용하던 코드는 변경하지 않아도 된다는 것이다. ContextWrapper에서 Context 구현체를 바꾸는 메서드가 &lt;span style=&quot;color: #0593d3;&quot;&gt;attachBaseContext(Context base)&lt;/span&gt;이다.&lt;/span&gt;&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: #000000;&quot;&gt;&lt;b&gt;Context의 종류&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위의 Context 구조에서 볼 수 있듯이 ContextWrapper를 상속하는 컴포넌트는 Activity, Service, Application이다. Broadcast Reciever나 Content Provider는 ContextWrapper를 상속하지 않는다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 Acitvity, Service, Application 컴포넌트는 각각 ContextImpl을 생성하고 ContextWrapper에서 getBaseContext()와 getApplicationContext() 메서드를 사용해 Context를 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Application Context&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- 싱글톤 객체로 어플리케이션의 생명주기를 따른다. 즉, 앱의 시작 시 부터 종료 시 까지 살아있다.&lt;/span&gt;&lt;/p&gt;
&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Activity Context&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- Activity 자체가 Context를 상속하고 있기 때문에 Activity 인스턴스 자체가 Context 역할을 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;- 그렇기 때문에 Activity Context는 액티비티 생명주기를 따른다.&lt;/span&gt;&lt;/p&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: #000000;&quot;&gt;&lt;b&gt;Context Type&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Application&lt;/b&gt; - 싱글톤으로 어플리케이션 객체가 생성될 때 함께 Application Context가 생성되기 때문에 Application Context는 동일한 앱 안에서 항상 동일한 인스턴스를 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Activity / Service&lt;/b&gt; - 액티비티나 서비스가 생성될 때마다 각자의 Context 인스턴스가 생성된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Broadcast Receiver&lt;/b&gt; - 자기 자신이 Context는 아니다. 리시버가 브로드캐스트 처리를 할 때마다 Context를 onReceive()의 인자로 전달 받아서 사용한다. 전달 받은 Context의 생명주기를 따르기 때문에 액티비티 컨텍스트로 브로드캐스트 실행 시 액티비티가 종료되면 브로드캐스트도 함께 종료된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Content Provider&lt;/b&gt; - 자기 자신이 Context는 아니다. 동일한 응용 프로그램에 대해 호출 시 동일한 싱글톤 Context 객체를 반환하고, 서로 다른 응용 프로그램에 대해 호출 시 다른Context 객체를 반환한다.&lt;/span&gt;&lt;/p&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: #000000;&quot;&gt;&lt;b&gt;Context 참조 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ActivityName.this&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- Activity Context를 반환한다. Activity Scope 안에서 사용할 때는 this만으로도 가져올 수 있다.&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;Java에서는 Activity의 Inner Class와 같이 Activity Scope를 벗어난 곳에서 액티비티 인스턴스를 참조하고 싶을 때&lt;/span&gt; &lt;span style=&quot;color: #0593d3;&quot;&gt;ActivityName.this&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;로 가져올 수 있다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;마찬가지로 Kotlin에서는 higher order function을 많이 사용하기 때문에 Activity의 Scope를 벗어나는 경우가 자주 있는데 해당 Scope에서 액티비티 인스턴스를 가져오고 싶을 때&lt;/span&gt; &lt;span style=&quot;color: #0593d3;&quot;&gt;this@ActivityName&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&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;&lt;b&gt;getApplicationContext()&lt;br /&gt;-&amp;nbsp;&lt;/b&gt;Application Context를 반환한다.&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;getApplication()&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- Application 인스턴스를 반환한다. Application도 Context의 자식 클래스이므로 Context처럼 사용할 수 있다.&lt;br /&gt;- 하지만 Application과 Application Context가 항상 동일한 인스턴스라는 보장이 없고, Application은 Activity나 Service 내부에서만 참조가 가능하다는 차이점도 있다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;&lt;/b&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;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;getBaseContext()&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- ContextWrapper의 Context 인스턴스를 반환한다. 다른 Context를 참조해야 하는 경우, 그 ContextWrapper 안에 있는 context를 getBaseContext()로 가져와 사용한다. 이는 Activity Context의 일종이다.&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;View의 getContext()&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;View에도 getContext() 메서드가 있어 Context를 가져올 수 있는데, View를 생성할 때 생성자의 인자로 들어가는 Context가 getContext()의 결과로 반환된다. 일반적으로 View가 속해 있는 Activity의 Context가 해당 View의 Context가 된다.&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;span style=&quot;color: #333333;&quot;&gt;Context의 생명주기가 다르기 때문에 Activity와 분리된 작업에 Activity Context를 사용하면 예상치 못한 동작이 발생할 수 있다. 반대로 Activity에 종속된 작업을 Application Context를 이용하면 Activity가 종료되어도 Context를 이용한 작업이 종료되지 않아 memory leak이 발생할 수도 있다. 그렇기 때문에 Context를 사용할 때 각 Context의 특징과 Context가 필요한 작업의 특성을 고려해 Context를 알맞게 선택해 사용해야 한다.&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;&lt;span style=&quot;color: #333333;&quot;&gt;[참고한 사이트]&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://black-jin0427.tistory.com/220&quot;&gt;&lt;span&gt;[Android, Context] Context 넌 무엇이더냐?&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Mobile/Android</category>
      <category>Android</category>
      <category>ApplicationContext</category>
      <category>BaseContext</category>
      <category>ContextImpl</category>
      <category>ContextWrapper</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/10</guid>
      <comments>https://s2choco.tistory.com/10#entry10comment</comments>
      <pubDate>Wed, 10 Jun 2020 15:23:58 +0900</pubDate>
    </item>
    <item>
      <title>[MSSQL] sa 계정 비밀번호 변경 방법</title>
      <link>https://s2choco.tistory.com/38</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SQL Server 인스턴스를 만들 때 인증 방식을 혼합 모드로 만들어서 sa 계정을 만들어 놓고 그 뒤로 윈도우 인증 방식으로만 접속하다보니 sa 계정 비밀번호를 잊어버리는 사태가 발생한 적이 있었다.. 이 때 sa 계정이 필요했던 이유는 윈도우 인증 방식이&lt;/span&gt; &lt;span style=&quot;color: #0593d3;&quot;&gt;[컴퓨터 이름]\[인스턴스 이름]&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;이번 포스팅은 sa 계정 비밀번호를 변경하기 위해 여러 번의 구글링과 몇 번의 오류를 해결한 뒤에 성공했던 여정을 기록한 글이다.&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: #333333;&quot;&gt;시작!&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;먼저, 컴퓨터 이름을 다시 기존 이름으로 바꾼 뒤 윈도우 인증 방식으로 SQL Server에 접속한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;[개체 탐색기]에서 서버 이름을 우클릭한 뒤 [속성] &amp;gt; [보안] &amp;gt; [서버 인증]에 들어가서 [SQL Server 및 Windows 인증모드(S)]로 인증 방법을 변경한다.&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-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;707&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drayFc/btqECunRzOA/P1cj5HEUalSFUnQpUkbgsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drayFc/btqECunRzOA/P1cj5HEUalSFUnQpUkbgsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drayFc/btqECunRzOA/P1cj5HEUalSFUnQpUkbgsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrayFc%2FbtqECunRzOA%2FP1cj5HEUalSFUnQpUkbgsk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;707&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;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 [개체 탐색기]에 있는 서버의 하위 목록인 [보안] &amp;gt; [로그인] 으로 들어가면 sa가 있는데 이를 더블클릭하면 아래 화면이 나온다. 여기서 암호를 변경한 뒤 SQL Server 구성관리자에서 인스턴스를 재시작하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;649&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/pbzkf/btqECu2tmFE/BDTAKXjSSQDjPM5Ut9Kxn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pbzkf/btqECu2tmFE/BDTAKXjSSQDjPM5Ut9Kxn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pbzkf/btqECu2tmFE/BDTAKXjSSQDjPM5Ut9Kxn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpbzkf%2FbtqECu2tmFE%2FBDTAKXjSSQDjPM5Ut9Kxn0%2Fimg.png&quot; width=&quot;649&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;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이후에 변경한 sa 계정으로 접속해도 &quot;접속할 수 없다&quot;라는 오류가 발생하는 경우, 암호가 제대로 변경되지 않을 수도 있기 때문에 cmd를 관리자모드로 실행 후 아래 명령어를 통해 암호를 변경해 줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;osql -E 는 윈도우 인증 방식으로 SQL Server 인스턴스 접속하라는 명령어이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591195471588&quot; class=&quot;html xml&quot; style=&quot;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; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &amp;gt; osql -E
  &amp;gt; EXEC sp_password NULL, '원하는 패스워드', 'sa'
  &amp;gt; GO&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;span style=&quot;color: #333333;&quot;&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;&lt;b&gt;&quot;서버에는 연결했지만 로그인 하는 동안 오류가 발생했습니다. (provider: 공유메모리 공급자. error: 0 - 파이프의 다른 끝에 프로세스가 없습니다.)&quot;&lt;/b&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;이를 해결하기 위해 SQL Server 구성관리자에 들어가서 아래 화면과 같이 네트워크 구성 &amp;gt; 프로토콜 &amp;gt; TCP/IP를 &quot;사용&quot; 으로 변경한 뒤 SQL Server 구성관리자에서 인스턴스를 재시작했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;490&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/bSk2D5/btqEA2sD750/OBKpYUzcQGOkHtKMhs16hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSk2D5/btqEA2sD750/OBKpYUzcQGOkHtKMhs16hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSk2D5/btqEA2sD750/OBKpYUzcQGOkHtKMhs16hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSk2D5%2FbtqEA2sD750%2FOBKpYUzcQGOkHtKMhs16hk%2Fimg.png&quot; width=&quot;490&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;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게까지 했는데도 위와 동일한 오류가 발생한다면, cmd 관리자모드에서 아래 명령어를 입력한다. 이는 인터넷 연결 문제가 있을 때 TCP/IP 설정 정보들을 초기상태로 돌리는 작업이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591197689460&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; netsh Winsock reset&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;그리고 나서 컴퓨터를 재부팅한 후 또 다시 SQL Server 구성관리자에서 인스턴스를 재시작한다.&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;그렇게 다시한번 sa 계정으로 접속해 보았으나 다음의 오류가 발생했다.&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;&quot;[ 오류 : 18470 ] 계정을 사용할 수 없습니다.&quot;&lt;/b&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;이는 sa 계정으로 로그인이 비활성화 되었기 때문에 발생하는 오류로, SQL Server에 윈도우 인증방식으로 접속 후 쿼리 창에 아래 쿼리를 실행해 sa계정을 활성화 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591196474383&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ALTER LOGIN [sa] ENABLE&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;이렇게 하고 다시 sa 계정으로 로그인하니 그제서야 접속에 성공했다!!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>MSSQL</category>
      <category>sa</category>
      <category>SQL Server</category>
      <category>변경</category>
      <category>비밀번호</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/38</guid>
      <comments>https://s2choco.tistory.com/38#entry38comment</comments>
      <pubDate>Thu, 4 Jun 2020 00:15:58 +0900</pubDate>
    </item>
    <item>
      <title>SQLD 과목 II 정리 노트</title>
      <link>https://s2choco.tistory.com/37</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;작년에 SQLD 자격증을 준비하면서 과목 II 파트를 에버노트에 열심히 정리해 놓은 걸 보니 뭔가 아까운 마음에 공유하려고 한다.&lt;/span&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(과목 I 파트 정리본도 있지만 그 파트는 공부를 시작하면서 패기롭게 노트에 직접 수기로 정리했기 때문에 공유하고 싶어도 참 어렵다...ㅎ) &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이는 내용에 대한 설명 보다는 요약 및 정리가 많기 때문에, 전체적인 공부를 끝낸 후에 시험 범위에 대한 내용을 상기시키는 용도로 활용하면 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Apvz_nz8Z9dD9aq0goGWl3_W8l5tFM_wDJ0&quot;&gt;[SQLD 과목Ⅱ] 1-1. 관계형 데이터베이스 개요&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApvWLpDAt-NElreetN0OImIXPpTMRk3Yx6g&quot;&gt;[SQLD 과목Ⅱ] 1-2. DDL(Data Definition Language)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/AptghYl8H8hAjL5EL6hLSeyaHnruowTINXM&quot;&gt;[SQLD 과목Ⅱ] 1-3. DML(Data Manipulation Language)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Apvo2qxqmBlHKoPVL566D4apHI374Q2zK8M&quot;&gt;[SQLD 과목Ⅱ] 1-4. TCL(Transaction Control Language)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Apu9AUiHhBVOFrZDYAQcLBPjE4Djod5SoJI&quot;&gt;[SQLD 과목Ⅱ] 1-5. WHERE 절&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Apt8DrUWDfhHJLXg6TQDnHDOdYbmEh2TTHE&quot;&gt;[SQLD 과목Ⅱ] 1-6. 함수(FUNCTION)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Apv465EKeBFNbq-leTTnXTq43P6ET2ve1qw&quot;&gt;[SQLD 과목Ⅱ] 1-7. GROUP BY, HAVING 절&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApvgNXqres1GKYvOqUeR_OFATdERXI6buTk&quot;&gt;[SQLD 과목Ⅱ] 1-8. ORDER BY 절&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Aps7g4jVq2lIX7EMwH0dBmHAG3OeFhU_j3U&quot;&gt;[SQLD 과목Ⅱ] 1-9. 조인(JOIN)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/AptjlVpaB1VKirx3M1_nMU7iU48KOP6eIzo&quot;&gt;[SQLD 과목Ⅱ] 2-1. 표준 조인(STANDARD JOIN)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/Apv9oxfsKpdInIEpYu5YpmXF3CN-HHw0-IM&quot;&gt;[SQLD 과목Ⅱ] 2-2. 집합 연산자(SET OPERATOR)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApuHAZ_cmbFACIJ00NFX-qkAnHkO8rhA3Nw&quot;&gt;[SQLD 과목Ⅱ] 2-3. 계층형 질의와 셀프 조인&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/AptznRcxK2hPorTaPInBwqi4JB5_SgVVDKQ&quot;&gt;[SQLD 과목Ⅱ] 2-4. 서브쿼리&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApujA_M3nHtJJbcdATQ8wqHJ8M-UYMFrmQE&quot;&gt;[SQLD 과목Ⅱ] 2-5. 그룹 함수(GROUP FUNCTION)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/AptubIw6z8ROCK4m6EObd3PTBxKWDMxvekE&quot;&gt;[SQLD 과목Ⅱ] 2-6. 윈도우 함수(WINDOW FUNCTION)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApujCz3Za2VLG4WY9yZSUhrQi2JpcD7sd9A&quot;&gt;[SQLD 과목Ⅱ] 2-7. DCL(Data Control Language)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApuEiKUfI9xHt6R0ICzkXmxAfLgwFb4546w&quot;&gt;[SQLD 과목Ⅱ] 2-8. 절차형 SQL&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApvWUyi9wfBMg7dnCCLB7US23u6kGNS_6B0&quot;&gt;[SQLD 과목Ⅱ] 3-1. 옵티마이저와 실행계획&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApvaDreeGYJH67Ixt7qJlM4_xmNg3lUI-Ao&quot;&gt;[SQLD 과목Ⅱ] 3-2. 인덱스 기본&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.evernote.com/l/ApuqobQM2TZIPbSFivtl8QkfNwOa1H9RCCs&quot;&gt;[SQLD 과목Ⅱ] 3-3. 조인 수행 원리&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>SQLD</category>
      <category>정리노트</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/37</guid>
      <comments>https://s2choco.tistory.com/37#entry37comment</comments>
      <pubDate>Thu, 28 May 2020 15:01:53 +0900</pubDate>
    </item>
    <item>
      <title>[MSSQL] OUTPUT, APPLY 문법 정리</title>
      <link>https://s2choco.tistory.com/36</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;저번 포스팅에 이어 OUTPUT과 APPLY에 대해 정리하고 마무리 하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OUTPUT&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OUTPUT은 INSERT, UPDATE, DELETE, MERGE문에 의한 데이터 변경 작업의 결과를 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;작업과 동시에 확인&lt;/span&gt;할 수 있다. 원래 SELECT를 해야 작업 결과를 볼 수 있고 다른 DML문(INSERT/UPDATE/DELETE/MERGE)은 영향 받은 행의 숫자만 알려주고 영향 받은 데이터는 보여 주지 않는다. 하지만 OUTPUT 키워드와 inserted, deleted 테이블을 사용하면 영향 받은 데이터도 볼 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;inserted, deleted 테이블&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OUTPUT이나 Trigger에서 DML을 사용할 때 자동으로 변경되는 시스템 테이블로, 기존 테이블과 구조가 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;INSERT/UPDATE/DELETE/MERGE문 작업을 할 때마다 inserted/deleted 테이블이 새로 만들어진다. 하지만 이 테이블은 메모리에만 존재하기 때문에 금방 사라진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;431&quot; height=&quot;NaN&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/c7tLFP/btqEqs5Qman/JjXIo7RWYjvkF12D78K9jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7tLFP/btqEqs5Qman/JjXIo7RWYjvkF12D78K9jk/img.png&quot; data-alt=&quot;inserted, deleted 테이블에 들어가는 데이터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7tLFP/btqEqs5Qman/JjXIo7RWYjvkF12D78K9jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7tLFP%2FbtqEqs5Qman%2FJjXIo7RWYjvkF12D78K9jk%2Fimg.png&quot; width=&quot;431&quot; height=&quot;NaN&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;inserted, deleted 테이블에 들어가는 데이터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용 예시&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1590552110098&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT 
    INTO TABLE_A (name, score) 
    OUTPUT inserted.*
    VALUES ('John', 97)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;MERGE문은 마지막에 OUTPUT을 추가하면 영향 받은 데이터가 출력으로 나온다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1590552641854&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MERGE TARGET_T target
    USING SOURCE_T AS source (code, name)  
    ON (target.code = source.code)
    WHEN MATCHED THEN
        UPDATE SET name = source.name  
    WHEN NOT MATCHED THEN  
        INSERT (code, name)  
        VALUES (source.code, source.name)  
    OUTPUT deleted.*, inserted.*&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;참고로, MERGE에서 OUTPUT절을 사용하면 대상 테이블(target table)의 변경 내용을 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;임의의 순서&lt;/span&gt;로 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;INTO&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OUTPUT으로 얻은 결과를 INTO [테이블]로 테이블에 넣을 수 있다. 여기서 결과를 넣을 테이블은 이미 존재하는 테이블이어야 한다. OUTPUT 결과를 INTO로 테이블에 넣으면 OUTPUT 결과는 나오지 않고, INTO를 사용하지 않으면 OUTPUT의 결과가 출력된다. 즉, OUTPUT을 사용하면 트리거(Trigger) 없이도 데이터의 변경 사항을 기록할 수 있다.&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;아래 쿼리는 TABLE_A에 데이터를 넣으면 변경된 값을 LOG_TABLE에 추가하는 쿼리로, 변경 사항이 결과로 출력되지 않고 LOG_TABLE에 저장된다. LOG_TABLE은 이미 존재하는 테이블이어야 하므로 미리 테이블을 만들어 놓아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1590552955648&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT 
    INTO TABLE_A (name, score) 
    OUTPUT inserted.* INTO LOG_TABLE
    VALUES ('John', 97)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;APPLY&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&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-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;785&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sTAyZ/btqEsWYIpHH/luiVFg1NNwrVPpJKyc3yHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sTAyZ/btqEsWYIpHH/luiVFg1NNwrVPpJKyc3yHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sTAyZ/btqEsWYIpHH/luiVFg1NNwrVPpJKyc3yHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsTAyZ%2FbtqEsWYIpHH%2FluiVFg1NNwrVPpJKyc3yHK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;785&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;&lt;span style=&quot;color: #0593d3;&quot;&gt;CROSS APPLY&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;테이블 반환 함수 또는 하위쿼리의 결과가 있는 행만 출력한다. (=INNER JOIN)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;OUTER APPLY&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;테이블 반환 함수 또는 하위쿼리의 결과가 없는 행도 모두 출력한다. (=LEFT JOIN)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;장점&lt;/span&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;JOIN문과 다르게 오른쪽 테이블이 왼쪽 테이블의 열을 참조할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;NL Join 형태로 동작하기 때문에 아래 예제에서 볼 수 있듯, TOP N문을 사용하여 저장된 데이터량에 상관없이 늘 동일한 비용이 들어간다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;NL Join에 대해 알고 싶다면 &lt;/span&gt;&lt;a href=&quot;https://s2choco.tistory.com/32&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에 정리해 놓았으니 참고하길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;단점&lt;/span&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;NL Join 형태로 동작하기 때문에 대량 데이터 처리 시 비용이 높다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Join으로 처리가 가능한 경우는 굳이 APPLY 사용을 추천하지 않고, 반드시 필요한 경우에만 제한적으로 사용하기를 권장한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용 예시&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;어떤 글에 대한 정보를 가진 Article 테이블과 글에 달린 댓글에 대한 Article_comment 테이블이 있을 때, 어떤 글을 comment가 가장 많은 순으로 정렬해서 3개만 보여주는 쿼리는 CROSS APPLY를 사용하면 INNER JOIN을 사용했을 때 보다 쿼리 길이도 짧아지고&amp;nbsp;빠른 성능을 낼 수 있다.&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;두 테이블 구조는 다음과 같다. Article 테이블은 articleNo 인덱스가 있고, Article_comment 테이블은 articleNo, commentNo 인덱스가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1590551460592&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE Article (
           articleNo  int IDENTITY (1,1),
           title      varchar(1000),
           contents   varchar(1000)
)

CREATE TABLE Article_comment (
           articleNo      int NOT NULL,
           commentNo      int IDENTITY(1,1),
           comments       varchar(1000)
)&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;CROSS APPLY 구문&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1590550691727&quot; class=&quot;sql&quot; style=&quot;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; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- CROSS APPLY
SELECT a.articleNo, a.title, b.commentNo, b.comments
FROM Article a
CROSS APPLY
	( SELECT TOP 3 b.*
		FROM Article_comment b
		WHERE b.articleNo = a.articleNo
		ORDER BY b.commentNo DESC ) b
WHERE a.articleNo &amp;lt; 3&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;INNER JOIN 구문&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1590550694863&quot; class=&quot;sql&quot; style=&quot;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; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- INNER JOIN
SELECT articleNo, title, commentNo, comments
FROM(
	SELECT a.articleNo, a.title, b.commentNo, b.comments, RANK() OVER(PARTITION BY a.articleNo ORDER BY commentNo DESC) rownum
	FROM Article a
	INNER JOIN(
		 SELECT * FROM Article_comment
	) b
	ON a.articleNo = b.articleNo
) o
WHERE rownum &amp;lt;= 3 AND articleNo &amp;lt; 3
ORDER BY articleNo, rownum&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>apply</category>
      <category>MSSQL</category>
      <category>output</category>
      <category>SQL Server</category>
      <author>[Ellie]</author>
      <guid isPermaLink="true">https://s2choco.tistory.com/36</guid>
      <comments>https://s2choco.tistory.com/36#entry36comment</comments>
      <pubDate>Wed, 27 May 2020 13:24:51 +0900</pubDate>
    </item>
  </channel>
</rss>