<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발계발</title>
    <link>https://hello-gg.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 00:47:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>말하는호구마</managingEditor>
    <image>
      <title>개발계발</title>
      <url>https://tistory1.daumcdn.net/tistory/3593851/attach/46c9a27d06b9448fbd8d99685bc66051</url>
      <link>https://hello-gg.tistory.com</link>
    </image>
    <item>
      <title>Github fork한 repository 잔디 복사하기 </title>
      <link>https://hello-gg.tistory.com/71</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;fork받아온 repository에 작업할 일이 생겼는데, 커밋한 내역이 잔디에 반영되지 않았다 ㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔디에 집착하지 말자는 다짐과는 다르게 너무 집착하는 즁....^_^&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork한 repository는 PR이 머지되었을 때 비로소 나의 커밋으로 인정이 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔디가 심어지는 기준은 내 repository에 커밋을 해야 인정이 되는데, fork는 contribution으로 인정되지 않아 잔디가 심어지지 않는다.&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;검색해보면 bare를 사용해서 커밋을 옮기고 기존의 repository를 삭제를 하는 방식이 정말 많은데,,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나같은 경우에는 fork받아온 기존의 repository가 의미가 있기에 삭제하길 원치 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 잔디를 &lt;b&gt;내가 만든 임의의 repository로 복사&lt;/b&gt;하는 방법을 썼다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 새로운 repository를 만든다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나같은 경우에는 부끄러우니까  private repository를 생성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 repository를 로컬에 가져와야하는데, 이 때 githuh에서 제공하는 init커맨드들을 실행해서 초기화하면 안된다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2432&quot; data-origin-height=&quot;1360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbqI7b/btrOmVoWC9W/4L1g72wkpC1MNVgwskEYK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbqI7b/btrOmVoWC9W/4L1g72wkpC1MNVgwskEYK0/img.png&quot; data-alt=&quot;요 화면의 라인들!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbqI7b/btrOmVoWC9W/4L1g72wkpC1MNVgwskEYK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbqI7b%2FbtrOmVoWC9W%2F4L1g72wkpC1MNVgwskEYK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2432&quot; height=&quot;1360&quot; data-origin-width=&quot;2432&quot; data-origin-height=&quot;1360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요 화면의 라인들!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 라인들을 그대로 따는 경우가 많았어서 습관적으로 따왔지만 이렇게 하면 안된다 ㅠ.ㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 커맨드들을 실행하지 말고 단순 clone만 해와서 로컬에 세팅하자.&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 로컬에서 fork한 repository로 이동한 후 grass 커맨드를 입력한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서의 fork한 repository에서 내가 복사하고 싶은 브랜치로 checkout한 뒤 아래의 명령어를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1665492112942&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% git remote add grass [새로 만들었던 repository url]
% git push grass&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;push까지 해줘야 origin까지 반영이 되어 잔디가 반영된다  (너무 당연함  )&lt;/p&gt;</description>
      <category>Computer Engineering/Server</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/71</guid>
      <comments>https://hello-gg.tistory.com/71#entry71comment</comments>
      <pubDate>Tue, 11 Oct 2022 21:44:00 +0900</pubDate>
    </item>
    <item>
      <title>GitHub Contribution 잔디 추가 안되는 현상</title>
      <link>https://hello-gg.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;분명 커밋을 올렸는데도 불구하고 잔디가 채워지지 않는 현상을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔디에 집착하는 것을 지양하는 나지만 그래도 왠지 기분이 나쁘다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 생각해보니 꽤 오래전부터 있었던 문제였던 것 같고, 놓쳐버린 잔디가 꽤나 많을 듯하다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당장 고쳐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. user email 설정 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;contribution으로 인정 안되는 이유는 여럿이 있는데, 많은 사람들의 원인은 user의 email이 잘못 설정되어 있어서였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github에 등록된 Email은 오른쪽 상단 프로필을 통해 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;1058&quot; width=&quot;120&quot; height=&quot;286&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsgR2l/btrher9teUz/qlWyCiPE5WkaZ5IhGqrm4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsgR2l/btrher9teUz/qlWyCiPE5WkaZ5IhGqrm4K/img.png&quot; data-alt=&quot; &amp;amp;amp;gt;&amp;amp;amp;gt; Settings&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsgR2l/btrher9teUz/qlWyCiPE5WkaZ5IhGqrm4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsgR2l%2Fbtrher9teUz%2FqlWyCiPE5WkaZ5IhGqrm4K%2Fimg.png&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;1058&quot; width=&quot;120&quot; height=&quot;286&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; &amp;gt;&amp;gt; Settings&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;1036&quot; width=&quot;555&quot; height=&quot;271&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu5Y7f/btrhdW9HbF5/cYtHw1cIyn5DUKkllK2vU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu5Y7f/btrhdW9HbF5/cYtHw1cIyn5DUKkllK2vU1/img.png&quot; data-alt=&quot;&amp;amp;amp;gt;&amp;amp;amp;gt;Emails&amp;amp;amp;nbsp;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu5Y7f/btrhdW9HbF5/cYtHw1cIyn5DUKkllK2vU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu5Y7f%2FbtrhdW9HbF5%2FcYtHw1cIyn5DUKkllK2vU1%2Fimg.png&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;1036&quot; width=&quot;555&quot; height=&quot;271&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;&amp;gt;&amp;gt;Emails&amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 내 프로젝트에 등록된 이메일은 config를 통해 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633860090579&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git config user.email&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이메일이 잘못 설정되어 있다면 이메일을 바꿔보도록 하자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. respository url 설정 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 직면했던 문제의 원인이다,,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 내 프로젝트의 repository의 url은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/nahyunLee/java_practice.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/nahyunLee/java_practice.git &lt;/a&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;a href=&quot;https://github.com/nahyunLee/java_practice.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/nahyunLee/java_practicce.git&lt;/a&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;즉 origin의 주소가 잘못 설정되어 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;origin의 주소를 확인할 수 있는 방법은 아래와 같은 명령어릍 통해 config파일을 확인하는 방법이 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633860372456&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat .git/config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 git 설정 값들이 뜨는데 이 중 origin주소를 확인하여 문제점을 발견하였다.&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;pre id=&quot;code_1633860434211&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git remote set-url origin [https://github.com/nahyunLee/java_practice.git]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소는 본인의 repository의 url를 삽입하면 될 것이다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바보짓을 하는 바람에 나의 아까운 잔디들을 낭비했다...( &lt;s&gt;잔디에 집착 안하는거 맞냐고,,,&lt;/s&gt;)&lt;/p&gt;</description>
      <category>Computer Engineering</category>
      <category>contribution</category>
      <category>git</category>
      <category>Github</category>
      <category>java</category>
      <category>repository</category>
      <category>Shell</category>
      <category>잔디</category>
      <category>잔디심기</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/66</guid>
      <comments>https://hello-gg.tistory.com/66#entry66comment</comments>
      <pubDate>Sun, 10 Oct 2021 19:09:34 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot / Java 서버 애플로그인 api 연동</title>
      <link>https://hello-gg.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2020년 4월부터 iOS앱에서 소셜로그인만을 지원하는 앱은 애플로그인을 필수적으로 제공해야한다는 정책이 추가되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1631454944778&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;App Store Review Guidelines - Apple Developer&quot; data-og-description=&quot;App Store Review Guidelines Apps are changing the world, enriching people&amp;rsquo;s lives, and enabling developers like you to innovate like never before. As a result, the App Store has grown into an exciting and vibrant ecosystem for millions of developers and &quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&quot; data-og-url=&quot;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;App Store Review Guidelines - Apple Developer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App Store Review Guidelines Apps are changing the world, enriching people&amp;rsquo;s lives, and enabling developers like you to innovate like never before. As a result, the App Store has grown into an exciting and vibrant ecosystem for millions of developers and&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱을 출시하기 위해서는 심사를 거치게 되는데, iOS에서는 애플로그인이 없는 앱은 리젝당한다...&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전에 카카오 로그인, 구글 로그인 모두 서버 연동 구현을 해봤지만 애플은 타 소셜로그인 api와 매우매우 상이하다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오, 구글 로그인은 access token을 해당 회사의 api로 던져주면 바로 회원정보를 얻을 수 있는 형태이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 애플은 상대적으로 복잡하고 용어들이 조금 달라 조금 헤맸다...^^^**&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서도 그리 친절한 편이 아니라 개발자들의 원성이 자자한 것 같다,,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 정말 어렵지는 않다. 하지만 구글링을 통해 봤던 블로그들이 내가 생각했던 것 보다 복잡하게 되어있어서 조금 당황했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다른 블로그들의 코드를 인용하기 보단, 내가 필요한 부분만 구현하기 위해 방법만 숙지하고 직접 코드를 작성했다...ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&lt;b&gt;아직 미숙한 주니어가 대충쓴 코드라 부끄럽지만,,, 삽질 기록과 함께 혹시나 나같은 개발자들에게 미약한 도움이 될까 싶어 작성하게 되었다.&amp;nbsp;&lt;/b&gt;&lt;/s&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는&lt;b&gt; iOS 클라이언트로 부터 token 인증 요청을 받고, Spring boot 서버를 통해 애플로그인 api연동을 구현&lt;/b&gt;했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체적인 로그인 토큰을 사용하기 때문에 사용자의 정보만이 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.&amp;nbsp; 연동 프로세스&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보기에 앞서 애플로그인 연동의 플로우를 파악하는게 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. iOS 클라이언트로부터 identity token을 받는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; --&amp;gt; 이 시점에서 내가 헤맸던 점은, 용어이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;다른 소셜 로그인들은 access token이란 것을 받지만 애플로그인에서는 identity token이 그를 대신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;iOS에서는 &lt;b&gt;[authorization code, &lt;b data-stringify-type=&quot;bold&quot;&gt;identity token] &lt;/b&gt;&lt;/b&gt;이 두가지를 받을 수 있는데, 이 중 서버에서는 identity token을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. apple api를 통해 공개키 3개를 받아온다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 클라이언트로부터 받은 identity token을 decode한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 3개의 공개키 중, 클라이언트에서 받은 identity token의 kid, alg 값이 같은 공개키를 찾는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 그 공개키들의 재료들로 새로운 공개키를 만들고, 이 공개키로 JWT토큰 바디부분을 decode하면 유저 정보 확인 가능하다 .&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.&amp;nbsp; 구현&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 총 두가지 방식을 보여주고자 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;첫번째 방법&lt;/b&gt;은, 애플로부터 response받은 정보가 JSON형식임을 이용하는 방법이다. 처음 구현할 때, 시간이 촉박하고 전체적인 구조가 파악이 덜 됐을 때, 구현했던 방식이다. 어떤 애플로부터 정보들을 가져와야하는지 잘 모르는 상태에서, 일단 받아보고 데이터를 파악할 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두번째 방법&lt;/b&gt;은, 첫번째 방식의 코드를 배포하고 리팩토링한 결과이다. 생으로 JSON을 파싱하는 것보다 DTO로 매핑해서 오는 방법이다. 가독성 증가 효과를 기대한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. JSON 형식을 사용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. iOS 클라이언트로부터 identity token을 받는다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2246&quot; data-origin-height=&quot;724&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blEw5g/btreKkSSwww/BtduRdLsV1bqxGvmU4Xry0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blEw5g/btreKkSSwww/BtduRdLsV1bqxGvmU4Xry0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blEw5g/btreKkSSwww/BtduRdLsV1bqxGvmU4Xry0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblEw5g%2FbtreKkSSwww%2FBtduRdLsV1bqxGvmU4Xry0%2Fimg.png&quot; data-origin-width=&quot;2246&quot; data-origin-height=&quot;724&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; 박스친 부분만 확인하면 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여느 로그인 기능과 같이 identity token은 RequestHeader로 받아왔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 identity token이 들어왔다면 이를 서비스단으로 전달한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더욱 쉬운 이해를 위해 Service 전체 코드를 앞서 배치했다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631457999243&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class AppleService {

    /**
     * 1. apple로 부터 공개키 3개 가져옴
     * 2. 내가 클라에서 가져온 token String과 비교해서 써야할 공개키 확인 (kid,alg 값 같은 것)
     * 3. 그 공개키 재료들로 공개키 만들고, 이 공개키로 JWT토큰 부분의 바디 부분의 decode하면 유저 정보
     */
    public String userIdFromApple(String idToken) {
        StringBuffer result = new StringBuffer();
        try {
            URL url = new URL(&quot;https://appleid.apple.com/auth/keys&quot;);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(&quot;GET&quot;);
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String line = &quot;&quot;;

            while ((line = br.readLine()) != null) {
                result.append(line);
            }
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.FAILED_TO_VALIDATE_APPLE_LOGIN);
        }

        JsonParser parser = new JsonParser();
        JsonObject keys = (JsonObject) parser.parse(result.toString());
        JsonArray keyArray = (JsonArray) keys.get(&quot;keys&quot;);


        //클라이언트로부터 가져온 identity token String decode
        String[] decodeArray = idToken.split(&quot;\\.&quot;);
        String header = new String(Base64.getDecoder().decode(decodeArray[0]));

        //apple에서 제공해주는 kid값과 일치하는지 알기 위해
        JsonElement kid = ((JsonObject) parser.parse(header)).get(&quot;kid&quot;);
        JsonElement alg = ((JsonObject) parser.parse(header)).get(&quot;alg&quot;);

        //써야하는 Element (kid, alg 일치하는 element)
        JsonObject avaliableObject = null;
        for (int i = 0; i &amp;lt; keyArray.size(); i++) {
            JsonObject appleObject = (JsonObject) keyArray.get(i);
            JsonElement appleKid = appleObject.get(&quot;kid&quot;);
            JsonElement appleAlg = appleObject.get(&quot;alg&quot;);

            if (Objects.equals(appleKid, kid) &amp;amp;&amp;amp; Objects.equals(appleAlg, alg)) {
                avaliableObject = appleObject;
                break;
            }
        }

        //일치하는 공개키 없음
        if (ObjectUtils.isEmpty(avaliableObject))
            throw new BusinessException(ErrorCode.FAILED_TO_FIND_AVALIABLE_RSA);

        PublicKey publicKey = this.getPublicKey(avaliableObject);

        //--&amp;gt; 여기까지 검증

        Claims userInfo = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(idToken).getBody();
        JsonObject userInfoObject = (JsonObject) parser.parse(new Gson().toJson(userInfo));
        JsonElement appleAlg = userInfoObject.get(&quot;sub&quot;);
        String userId = appleAlg.getAsString();

        return userId;
    }

    public PublicKey getPublicKey(JsonObject object) {
        String nStr = object.get(&quot;n&quot;).toString();
        String eStr = object.get(&quot;e&quot;).toString();

        byte[] nBytes = Base64.getUrlDecoder().decode(nStr.substring(1, nStr.length() - 1));
        byte[] eBytes = Base64.getUrlDecoder().decode(eStr.substring(1, eStr.length() - 1));

        BigInteger n = new BigInteger(1, nBytes);
        BigInteger e = new BigInteger(1, eBytes);

        try {
            RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n, e);
            KeyFactory keyFactory = KeyFactory.getInstance(&quot;RSA&quot;);
            PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
            return publicKey;
        } catch (Exception exception) {
            throw new BusinessException(ErrorCode.FAILED_TO_FIND_AVALIABLE_RSA);
        }
    }
}&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;b&gt;2. apple api를 통해 공개키 3개를 받아온다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;642&quot; width=&quot;708&quot; height=&quot;312&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tnoPQ/btrePWCZWHs/0PJWZd48hfvCR7ZGRMSgjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tnoPQ/btrePWCZWHs/0PJWZd48hfvCR7ZGRMSgjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tnoPQ/btrePWCZWHs/0PJWZd48hfvCR7ZGRMSgjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtnoPQ%2FbtrePWCZWHs%2F0PJWZd48hfvCR7ZGRMSgjk%2Fimg.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;642&quot; width=&quot;708&quot; height=&quot;312&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;result를 확인해보면 아래와 같은 데이터를 얻을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;{&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: [&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;kty&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;RSA&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;kid&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;eXaunmL&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;use&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;sig&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;alg&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;RS256&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;AQAB&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;},&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;{&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;kty&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;RSA&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;kid&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;86D88Kf&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;use&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;sig&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;alg&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;RS256&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;e&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;AQAB&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;},&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;{&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;kty&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;RSA&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&quot;kid&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;YuyXoY&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&quot;use&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;sig&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&quot;alg&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;RS256&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&quot;n&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;1JiU4l3YCeT4o0gVmxGTEK1IXR-Ghdg5Bzka12tzmtdCxU00ChH66aV-4HRBjF1t95IsaeHeDFRgmF0lJbTDTqa6_VZo2hc0zTiUAsGLacN6slePvDcR1IMucQGtPP5tGhIbU-HKabsKOFdD4VQ5PCXifjpN9R-1qOR571BxCAl4u1kUUIePAAJcBcqGRFSI_I1j_jbN3gflK_8ZNmgnPrXA0kZXzj1I7ZHgekGbZoxmDrzYm2zmja1MsE5A_JX7itBYnlR41LOtvLRCNtw7K3EFlbfB6hkPL-Swk5XNGbWZdTROmaTNzJhV-lWT0gGm6V1qWAK2qOZoIDa_3Ud0Gw&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #a31515;&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&quot;e&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #0451a5;&quot;&gt;&quot;AQAB&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;background-color: #fafafa; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; ]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keys라는 jsonArray를 받을 수 있고 3개의 공개키로 구성되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 클라이언트로부터 받은 identity token을 decode한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;318&quot; width=&quot;691&quot; height=&quot;176&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coDtdz/btrePW34wer/FEdKXugdqXSCnjmxmG0fAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coDtdz/btrePW34wer/FEdKXugdqXSCnjmxmG0fAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coDtdz/btrePW34wer/FEdKXugdqXSCnjmxmG0fAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoDtdz%2FbtrePW34wer%2FFEdKXugdqXSCnjmxmG0fAK%2Fimg.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;318&quot; width=&quot;691&quot; height=&quot;176&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;identity token은 jwt토큰이므로 decode하면 header, payload를 얻을 수 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 우리는 &lt;b&gt;헤더의 kid,alg 데이터의 값이 필요&lt;/b&gt;하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 알아내서 이전에 애플로부터 얻었던 공개키들 중 우리에게 필요한 공개키가 무엇인지를 알아낼 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 3개의 공개키 중, 클라이언트에서 받은 identity token의 kid, alg 값이 같은 공개키를 찾는다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;688&quot; width=&quot;618&quot; height=&quot;332&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sKowS/btreVxPYNYW/q11VUq12S5qwDYRGbTGq01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sKowS/btreVxPYNYW/q11VUq12S5qwDYRGbTGq01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sKowS/btreVxPYNYW/q11VUq12S5qwDYRGbTGq01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsKowS%2FbtreVxPYNYW%2Fq11VUq12S5qwDYRGbTGq01%2Fimg.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;688&quot; width=&quot;618&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;3개의 공개키가 JsonArray로 배열안에 있었기 때문에 내부의 JsonObject(공개키)를 하나씩 확인해보는 과정이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JsonObject의 &quot;kid&quot;,&quot;alg&quot;를 Element형식으로 뽑아내고, identity token으로 부터 얻은 kid, alg값과 비교한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 kid, alg가 일치하는 공개키를 찾는다면 이를 avaliableObject를 통해 가지고 나온다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 반복문을 다 돌았는데 일치하는 공개키가 없어 avaliableObject가 여전히 null을 띄고 있다면 Exception을 발생시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 &lt;b&gt;우리가 사용할 공개키는 avaliableObject에 담겼다&lt;/b&gt;!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 그 공개키들의 재료들로 새로운 공개키를 만들고, 이 공개키로 JWT토큰 바디부분을 decode하면 유저 정보 확인 가능하다 .&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;438&quot; width=&quot;695&quot; height=&quot;194&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXVCtx/btreLfKh0xG/x6673gHGKFUsCnzqdtFhB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXVCtx/btreLfKh0xG/x6673gHGKFUsCnzqdtFhB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXVCtx/btreLfKh0xG/x6673gHGKFUsCnzqdtFhB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXVCtx%2FbtreLfKh0xG%2Fx6673gHGKFUsCnzqdtFhB0%2Fimg.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;438&quot; width=&quot;695&quot; height=&quot;194&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;810&quot; width=&quot;685&quot; height=&quot;369&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/my1Jk/btreJ0zKLTc/EUinXhw3OSFgD7E2NwgQb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/my1Jk/btreJ0zKLTc/EUinXhw3OSFgD7E2NwgQb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/my1Jk/btreJ0zKLTc/EUinXhw3OSFgD7E2NwgQb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmy1Jk%2FbtreJ0zKLTc%2FEUinXhw3OSFgD7E2NwgQb1%2Fimg.png&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;810&quot; width=&quot;685&quot; height=&quot;369&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;b&gt;&quot;n&quot;,&quot;e&quot;의 데이터들도 새로운 공개키를 만들 것&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 정보들은 인코딩되어 있는 상태기 때문에 decode해서 사용하는 과정이 꼭 필요하다.&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;(DTO로 매핑하지 않아서, 문자열을 수정하는 코드들이 포함되어 있다. 사실 정말 나쁜코드이다 ^^^;;;&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;이렇게 새로운 공개키를 만들고, 이를 다시 decode하여&lt;b&gt; &quot;sub&quot;의 데이터를 통해 계정 고유아이디를 확인할 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 얻은 userId를 사용해 우리 프로젝트가 발행하는 고유의 토큰으로 만들 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2.DTO로 매핑해오는 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 많은 블로그들이 포스팅했던 방식이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 소개 했던 방법보다 훨씬 좋은 코드가 될 것이다. 남들이 봐도 무슨 데이터를 가져오고자 하는지 명확하게 보인다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(이렇게 DTO의 중요성을 한번더,,)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 처음 구현할 때는 어떤 데이터들이 오는지 정확히 모르기 때문에 처음부터 이 방법 조금 무리였다고 판단했다,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 다른 블로그들을 보고 따라한다면 했겠지만 뭔지도 모르고 따라하기는 싫었다.&lt;/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;--&amp;gt; 작성중&lt;/p&gt;</description>
      <category>Computer Engineering/Server</category>
      <category>API</category>
      <category>apple</category>
      <category>appleLogin</category>
      <category>IOS</category>
      <category>java</category>
      <category>JPA</category>
      <category>restapi</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>애플로그인</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/65</guid>
      <comments>https://hello-gg.tistory.com/65#entry65comment</comments>
      <pubDate>Mon, 13 Sep 2021 00:29:48 +0900</pubDate>
    </item>
    <item>
      <title>EC2에 Springboot 배포시 RDS연결 불가 문제</title>
      <link>https://hello-gg.tistory.com/63</link>
      <description>&lt;p&gt;이전에는 잘 되던 배포가 갑자기 안되기 시작했다....&lt;/p&gt;
&lt;p&gt;갑자기라는건 없지만... 나한테는 갑작스러운 문제였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAW0vm/btq090MXwkM/VD5XEWPRyEM7PJkLVc9ziK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAW0vm/btq090MXwkM/VD5XEWPRyEM7PJkLVc9ziK/img.png&quot; data-alt=&quot; 15:47:09.707 ERROR [File:HikariPool.java] [Func:throwPoolInitializationException] [Line:593] - HikariPool-1 - Exception during pool initialization. com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAW0vm/btq090MXwkM/VD5XEWPRyEM7PJkLVc9ziK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAW0vm%2Fbtq090MXwkM%2FVD5XEWPRyEM7PJkLVc9ziK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 15:47:09.707 ERROR [File:HikariPool.java] [Func:throwPoolInitializationException] [Line:593] - HikariPool-1 - Exception during pool initialization. com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;오류를 살펴보니 MySQL connection문제임을 알 수 있다.&lt;/p&gt;
&lt;p&gt;RDS의 MySQL을 연결해둔 상태였는데 갑자기 안됐다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 문제에 대해서는 여러가지 원인이 있겠지만 나같은 경우 EC2에 올라와있는 jar파일이 RDS로 접근할 수 없었던게 이유였다.&lt;/p&gt;
&lt;p&gt;생각해보니 전에 RDS인바운드 보안 규칙을 팀원들의 ip로 한정지었던게 생각이 났다...!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;같은 VPC를 사용중이라 당연히 될 줄 알았지만 크나큰 오해였다...!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;EC2의 private IP를 RDS의 인바운드규칙 소스에 추가해주면 해결이 된다..!&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;여기서 중요한 점은 publicIP가 아닌 privateIP를 추가해줘야 한다는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;같은 VPC를 사용하니 당연한 얘기겠지만 이를 간과하고 문제를 해결하려다가 시간낭비를 했다  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;지금까지 보안이 크게 중요하다고 생각하지 못했어서 계속 모든 IP들을 열어놨었다.&lt;/p&gt;
&lt;p&gt;새로운....삽질....&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Engineering/Server</category>
      <category>aws</category>
      <category>EC2</category>
      <category>java</category>
      <category>mysql</category>
      <category>rds</category>
      <category>springboot</category>
      <category>Subnet</category>
      <category>VPC</category>
      <category>보안그룹</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/63</guid>
      <comments>https://hello-gg.tistory.com/63#entry63comment</comments>
      <pubDate>Sun, 28 Mar 2021 01:10:36 +0900</pubDate>
    </item>
    <item>
      <title>Springboot Querydsl 설치, JpaQueryFactory 설정</title>
      <link>https://hello-gg.tistory.com/62</link>
      <description>&lt;p&gt;Springboot에서 JPA를 많이 사용하지만 JPA로 복잡한 쿼리를 수행하기에는 무리가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;querydsl를 사용하면 보다 간편하게 쿼리를 수행할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&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;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;build.gradle&lt;/b&gt;에 추가할 부분&lt;/h3&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;515&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8NBNJ/btqZFtwdzFd/o3NbNV66fKih2NY7Q28P1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8NBNJ/btqZFtwdzFd/o3NbNV66fKih2NY7Q28P1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8NBNJ/btqZFtwdzFd/o3NbNV66fKih2NY7Q28P1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8NBNJ%2FbtqZFtwdzFd%2Fo3NbNV66fKih2NY7Q28P1k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;515&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;figure class=&quot;imageblock alignCenter&quot; width=&quot;491&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/cBmgfG/btqZMnVyNag/QvqZ2auMMmhJ3MCZnbMF7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBmgfG/btqZMnVyNag/QvqZ2auMMmhJ3MCZnbMF7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBmgfG/btqZMnVyNag/QvqZ2auMMmhJ3MCZnbMF7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBmgfG%2FbtqZMnVyNag%2FQvqZ2auMMmhJ3MCZnbMF7k%2Fimg.png&quot; width=&quot;491&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;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;496&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xWU5U/btqZFuPstIS/eGPf8xAFyWy1IojgEjB6FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xWU5U/btqZFuPstIS/eGPf8xAFyWy1IojgEjB6FK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xWU5U/btqZFuPstIS/eGPf8xAFyWy1IojgEjB6FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxWU5U%2FbtqZFuPstIS%2FeGPf8xAFyWy1IojgEjB6FK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;496&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615291976404&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    //for plugin
    id &quot;com.ewerk.gradle.plugins.querydsl&quot; version &quot;1.0.10&quot;
    
    
    
    //for dependencies
    implementation 'com.querydsl:querydsl-jpa'
    
    
    
    //querydsl설정

def querydslDir = &quot;$buildDir/generated/querydsl&quot;

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDir querydslDir
}

configurations {
    querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;jpaqueryfactory&lt;/b&gt;를 쓰기 위한 configuration&lt;/h3&gt;
&lt;p&gt;이 과정을 통해 jpaQueryFactory를 주입받아 어디서든 사용이 가능하다!!!&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;623&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBtl9U/btqZIzpctEj/InOKnjngjm7vkmPtdNARCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBtl9U/btqZIzpctEj/InOKnjngjm7vkmPtdNARCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBtl9U/btqZIzpctEj/InOKnjngjm7vkmPtdNARCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBtl9U%2FbtqZIzpctEj%2FInOKnjngjm7vkmPtdNARCK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;623&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615292102300&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class QuerydslConfiguration {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory(){
        return new JPAQueryFactory(entityManager);
    }

}
&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;이러고 gradle변경사항을 compile하면 끝 &lt;/p&gt;</description>
      <category>Computer Engineering/Server</category>
      <category>java</category>
      <category>JPA</category>
      <category>logic</category>
      <category>QueryDSL</category>
      <category>springboot</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/62</guid>
      <comments>https://hello-gg.tistory.com/62#entry62comment</comments>
      <pubDate>Tue, 9 Mar 2021 21:15:50 +0900</pubDate>
    </item>
    <item>
      <title>백준[1213] -팰린드롬 만들기</title>
      <link>https://hello-gg.tistory.com/61</link>
      <description>&lt;p&gt;팰린드롬이란 거꾸로 읽어도 제대로 읽는 것과 형태가 같은 문장을 이야기한다&lt;/p&gt;
&lt;p&gt;(내이름은 이효리~ 거꾸로해도 이효리~)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;진짜 작은 실수로 조금 오래걸렸다.&lt;/p&gt;
&lt;p&gt;그리고 Collection을 많이 써서 시간 초과가 날까 걱정했지만 이정도는 괜찮은 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;검색해보니 반례에 대해서 예민한 문제인 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;홀수개인 문자가 포함되어 있어도 이를 가운데 배치시키면 팰린드롬이 가능하다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;646&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddnPmH/btqYJ8S3sKq/yRKJJQXHeO1Ww9GCD86yFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddnPmH/btqYJ8S3sKq/yRKJJQXHeO1Ww9GCD86yFK/img.png&quot; data-alt=&quot;https://www.acmicpc.net/problem/1213&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddnPmH/btqYJ8S3sKq/yRKJJQXHeO1Ww9GCD86yFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddnPmH%2FbtqYJ8S3sKq%2FyRKJJQXHeO1Ww9GCD86yFK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;646&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;https://www.acmicpc.net/problem/1213&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;pre id=&quot;code_1614360227344&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;

public class Main {
    
    public static void main(String[] args) throws Exception {
       Scanner sc = new Scanner(System.in);
       String name = sc.nextLine();
       
       String result = &quot;&quot;;
       HashMap&amp;lt;Character, Integer&amp;gt; hash = new HashMap&amp;lt;&amp;gt;();
       ArrayList&amp;lt;Character&amp;gt; make = new ArrayList&amp;lt;&amp;gt;();
       
       for(int i=0;i&amp;lt;name.length();i++){
           hash.put(name.charAt(i),hash.getOrDefault(name.charAt(i),0)+1);
       }//해당 글자가 몇개 나왔는지 세기 위함 

       int over=0;
       String mid=&quot;&quot;;
       for(Character key: hash.keySet()){
           if(hash.get(key) %2 !=0){
               over++;
               if(over==2){
                   System.out.println(&quot;I'm Sorry Hansoo&quot;);
                   return;
               }//만약 홀수개 문자가 하나 이상이면 실패 (하나면 중간에 배치할 수 있음)
               
               mid+=key;//중간에 배치하기 위해 따로 뺴둠 
               if(hash.get(key)!=1) {
                   for (int i = 0; i &amp;lt; (hash.get(key)-1) / 2; i++) {
                       make.add(key);
                   }
               }
               /**만약 한 문자가 1개가 아닌 3개,5개.... 라면 가운데 올 문자 하나 빼고 
                나머지 문자들은 정렬을 해서 넣어야 사전 순으로 출력됨 
                */
               continue;
           }
           for(int i=0;i&amp;lt;hash.get(key)/2;i++) {
               make.add(key);
           }//글자가 짝수개라면 그냥 list에 넣어버리기 
       }
        Collections.sort(make);
       for(int i=0;i&amp;lt;make.size();i++){
           result+=make.get(i);
       }
       result+=mid;
       //홀수일 때 가운데 배치시키기 
       for(int i=make.size()-1;i&amp;gt;=0;i--){
           result+=make.get(i);
       }

       System.out.println(result);
   }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Engineering/알고리즘 테스트</category>
      <category>1213</category>
      <category>java</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>코테</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/61</guid>
      <comments>https://hello-gg.tistory.com/61#entry61comment</comments>
      <pubDate>Sat, 27 Feb 2021 02:25:44 +0900</pubDate>
    </item>
    <item>
      <title>백준[2805] java -나무자르기</title>
      <link>https://hello-gg.tistory.com/60</link>
      <description>&lt;p&gt;이 문제에서 알아야할 점은 두가지이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1.이분탐색으로 풀 것&lt;/p&gt;
&lt;p&gt;2.long타입 선언&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;603&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPhmgL/btqYvdnNcFd/ov1KKJJMuqf6TBxQyRXsTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPhmgL/btqYvdnNcFd/ov1KKJJMuqf6TBxQyRXsTk/img.png&quot; data-alt=&quot;https://www.acmicpc.net/problem/2805&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPhmgL/btqYvdnNcFd/ov1KKJJMuqf6TBxQyRXsTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPhmgL%2FbtqYvdnNcFd%2Fov1KKJJMuqf6TBxQyRXsTk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;603&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;https://www.acmicpc.net/problem/2805&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전에 공부했던 이분탐색을 생각해냈다.&lt;/p&gt;
&lt;p&gt;특정한 값을 구해야할 때 그 시간을 줄여주는 좋은 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이문제에서 long타입을 써야 하는 이유는 나무의 높이가 최대 1,000,000,000이기 때문이다&lt;/p&gt;
&lt;p&gt;int형으로는 계산할 수가 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1614258161807&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;
import java.util.stream.Collectors;

public class Main {
   public static void main(String[] args)  {
       Scanner sc = new Scanner(System.in);

      int n= sc.nextInt();
      long height = sc.nextInt();

      long[] tree = new long[n];
      long max=0;
      for(int i=0;i&amp;lt;n;i++){
          tree[i]= sc.nextInt();
          if(i==0){
            max = tree[i];
            continue;
          }
           if(tree[i]&amp;gt;max)
              max= tree[i];
      }

      long start = 0;
      long last =max;

       while(start&amp;lt;=last){
          long mid = (start+last)/2;
          long result=0;

          for(int i=0;i&amp;lt;n;i++){
              if(tree[i]&amp;gt;mid) {
                  result += tree[i] - mid;
              }
          }

          if(result&amp;gt;=height){
              start = mid+1;
          }
          else{
              last = mid-1;

          }
      }
        System.out.println(last);



   }


}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Engineering/알고리즘 테스트</category>
      <category>java</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>이분탐색</category>
      <category>코테</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/60</guid>
      <comments>https://hello-gg.tistory.com/60#entry60comment</comments>
      <pubDate>Thu, 25 Feb 2021 22:03:16 +0900</pubDate>
    </item>
    <item>
      <title>백준[10773]- 제로 (java)</title>
      <link>https://hello-gg.tistory.com/59</link>
      <description>&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;561&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGsutE/btqYpV1SXrS/lklOIeEKupKrou0Fh6IU1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGsutE/btqYpV1SXrS/lklOIeEKupKrou0Fh6IU1K/img.png&quot; data-alt=&quot;https://www.acmicpc.net/problem/10773&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGsutE/btqYpV1SXrS/lklOIeEKupKrou0Fh6IU1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGsutE%2FbtqYpV1SXrS%2FlklOIeEKupKrou0Fh6IU1K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;561&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;https://www.acmicpc.net/problem/10773&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1614192576349&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {

   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);

       int n= sc.nextInt();

       Stack&amp;lt;Integer&amp;gt; st =new Stack&amp;lt;&amp;gt;();
       long sum=0;

       for(int i=0;i&amp;lt;n;i++){
           int temp = sc.nextInt();
           if(temp==0) {
               if(!st.isEmpty()) {
                   st.pop();
               }
               else
                   continue;
           }
           else {
               st.push(temp);
           }
           }

       while(!st.isEmpty()){
           sum+=st.pop();
       }
       System.out.println(sum);
   }




}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Engineering/알고리즘 테스트</category>
      <category>java</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>코테</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/59</guid>
      <comments>https://hello-gg.tistory.com/59#entry59comment</comments>
      <pubDate>Thu, 25 Feb 2021 03:50:11 +0900</pubDate>
    </item>
    <item>
      <title>Springboot RESTAPI 아임포트 가상 결제</title>
      <link>https://hello-gg.tistory.com/58</link>
      <description>&lt;p&gt;Springboot로 REST API를 구현하는 중 가상결제 기능을 구현하고자 했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아임포트를 사용하여 카카오페이 결제 과정을 거쳐 실제 구매처럼 재연했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정말 정말 쉬운 과정이지만 RestAPI 구현중임을 간과해서,,,조금 오래 걸렸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 진행하는 프로젝트의 클라이언트는 iOS로 구현했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실제 결제화면을 iOS에서 확인하면&amp;nbsp; 좋았겠지만 팀의 사정상 그렇게 하지 못하고 웹에 내가 직접 구현했다.&lt;/p&gt;
&lt;p&gt;즉, &lt;b&gt;PC웹 환경에서 결제를 진행할 것이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 아임포트 홈페이지의 대쉬보드에 들어가고 회원가입,로그인을 해야한다!!!!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://admin.iamport.kr/users/login&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;admin.iamport.kr/users/login&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1614182920575&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 - 아임포트 관리자&quot; data-og-description=&quot;가입하신 Email주소로 비밀번호 변경 링크를 발송합니다.&quot; data-og-host=&quot;admin.iamport.kr&quot; data-og-source-url=&quot;https://admin.iamport.kr/users/login&quot; data-og-url=&quot;https://admin.iamport.kr/users/login&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://admin.iamport.kr/users/login&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://admin.iamport.kr/users/login&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;로그인 - 아임포트 관리자&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;가입하신 Email주소로 비밀번호 변경 링크를 발송합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;admin.iamport.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&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;style2&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;로그인을 하면 아래와 같은 페이지를 볼 수 있다&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[시스템 설정] -&amp;gt; [PG설정(일반결제 및 정기결제)] -&amp;gt; [PG사: 카카오페이 선택]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6GYTG/btqYn5qlLeQ/gYHxrEYbRGQhgeB85z1tm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6GYTG/btqYn5qlLeQ/gYHxrEYbRGQhgeB85z1tm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6GYTG/btqYn5qlLeQ/gYHxrEYbRGQhgeB85z1tm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6GYTG%2FbtqYn5qlLeQ%2FgYHxrEYbRGQhgeB85z1tm1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;테스트모드 ON을 하면 일반결제용 사이트코드와 정기결제용 사이트코드가 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;나는 일반 결제를 하기 위해 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;일반결제용 사이트코드를 가맹점코드(CID)에 입력해준다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 [내정보]에 들어가준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m5ta9/btqYn5KyVuf/Xl6L6eqVkWTpWT1bo5DK5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m5ta9/btqYn5KyVuf/Xl6L6eqVkWTpWT1bo5DK5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m5ta9/btqYn5KyVuf/Xl6L6eqVkWTpWT1bo5DK5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm5ta9%2FbtqYn5KyVuf%2FXl6L6eqVkWTpWT1bo5DK5k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;여기에 나오는 &lt;b&gt;가맹점 식별코드&lt;/b&gt;를 사용해야하니 복사를 해두자!!!!!!!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 클라이언트 구현을 해보자&lt;/p&gt;
&lt;p&gt;나는 html파일을 사용하여 정적으로 구현하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1614183611643&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Insert title here&amp;lt;/title&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot; src=&quot;https://code.jquery.com/jquery-1.12.4.min.js&quot; &amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot; src=&quot;https://cdn.iamport.kr/js/iamport.payment-1.1.5.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;script&amp;gt;

    $(function(){
    
    	//@@@@@@ 1번 @@@@@@@
        var IMP = window.IMP; // 생략가능
        IMP.init('가맹점 식별코드'); //가맹점 식별코드 삽입
        var msg;
        
        //@@@@@@@@ 2번 @@@@@@@@
        //url에서 parameter 가져오기 --&amp;gt; price값 알기 위해서
        var getParam = function(key){
            var _parammap = {};
            document.location.search.replace(/\??(?:([^=]+)=([^&amp;amp;]*)&amp;amp;?)/g, function () {
                function decode(s) {
                    return decodeURIComponent(s.split(&quot;+&quot;).join(&quot; &quot;));
                }

                _parammap[decode(arguments[1])] = decode(arguments[2]);
            });

            return _parammap[key];
        };
        
		//@@@@@@ 3번 @@@@@@@
            IMP.request_pay({
            pg : 'kakaopay',
            pay_method : 'card',
            merchant_uid : 'merchant_' + new Date().getTime(),
            name : 'vivipayment',
            amount : getParam(&quot;price&quot;),
            buyer_email : '이메일 넣기',
            buyer_name : '이름 넣기',
            buyer_tel : '번호 넣기',
            buyer_addr : '주소 넣기',
            buyer_postcode : '123-456',
           // m_redirect_url : '결제 완료후 이동할 페이지'
        }, function(rsp) {
            if ( rsp.success ) {
                //[1] 서버단에서 결제정보 조회를 위해 jQuery ajax로 imp_uid 전달하기
                jQuery.ajax({
                    url: &quot;/payments/complete&quot;, //cross-domain error가 발생하지 않도록 주의해주세요
                    type: 'POST',
                    dataType: 'json',
                    contentType: 'application/json',
                    data: JSON.stringify({
                        uid : rsp.imp_uid,
                        price: rsp.paid_amount
                        //기타 필요한 데이터가 있으면 추가 전달
                    })
                }).done(function(data) {
                    //[2] 서버에서 REST API로 결제정보확인 및 서비스루틴이 정상적인 경우
                    if ( everythings_fine ) {
                        msg = '결제가 완료되었습니다.';
                        msg += '\n고유ID : ' + rsp.imp_uid;
                        msg += '\n상점 거래ID : ' + rsp.merchant_uid;
                        msg += '\n결제 금액 : ' + rsp.paid_amount;
                        msg += '카드 승인번호 : ' + rsp.apply_num;

                        alert(msg);
                    } else {
                        //[3] 아직 제대로 결제가 되지 않았습니다.
                        //[4] 결제된 금액이 요청한 금액과 달라 결제를 자동취소처리하였습니다.
                    }
                });
                //성공시 이동할 페이지
                location.href='&amp;lt;%=request.getContextPath()%&amp;gt;/order/paySuccess?msg='+msg;
            } else {
                msg = '결제에 실패하였습니다.';
                msg += '에러내용 : ' + rsp.error_msg;
                //실패시 이동할 페이지
                location.href=&quot;&amp;lt;%=request.getContextPath()%&amp;gt;/order/payFail&quot;;
                alert(msg);
            }
        });

    });
&amp;lt;/script&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;웹은 잘 안하기 때문에 오랜만에 javascript를 했다.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;잠깐 코드 설명을 하자면!!!!!! (@@@@로 나뉜부분을 주목하자)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1번 --&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;아임포트에서의 내 정보를 입력해야한다.&lt;/p&gt;
&lt;p&gt;아까 기억해뒀던 가맹점 식별코드를 입력해야한다.&lt;/p&gt;
&lt;p&gt;&lt;s&gt;가맹점코드(CID)&lt;/s&gt;가 아니라 내정보에서의 &lt;b&gt;가맹점 식별코드&lt;/b&gt;를 입력해야한다!!!!(헷갈리지 않도록 주의)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2번 --&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;html url부터 parameter을 가져와 결제하고자 하는 금액 알려준다.&lt;/p&gt;
&lt;p&gt;클라이언트가 이전부터 차례대로 구현이 되어있다면 다른 방법이 있겠지만 이 페이지만 달랑 만든 것이기 때문에...&lt;/p&gt;
&lt;p&gt;정적인 html파일에 결제하고자 하는 값을 알려줘야했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리하여 url에 parameter형식으로 넘겨줬고 이를 parsing하는 과정을 추가했다.&lt;/p&gt;
&lt;p&gt;결론적으로 이 클라이언트에 접근할 수 있는 url은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcp29v/btqYurZYDoy/DlvYtaWMDlsTtH17EFPUu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcp29v/btqYurZYDoy/DlvYtaWMDlsTtH17EFPUu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcp29v/btqYurZYDoy/DlvYtaWMDlsTtH17EFPUu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcp29v%2FbtqYurZYDoy%2FDlvYtaWMDlsTtH17EFPUu0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;여기서 kakaoPayment.html에 코드를 작성했기 때문에&lt;/p&gt;
&lt;p&gt;도메인 주소를 통해 kakaoPayment.html을 호출해주고 파라미터로 price를 준다.&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;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재 서버는 REST API 구현을 위한 것이다.&lt;/p&gt;
&lt;p&gt;Springboot 를 사용하고 있기 때문에 다 @RestController어노테이션 설정을 통해 Controller가 구현되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;@RestController와 @Controller의 리턴 방식은 다르다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이를 숙지하고 url로 접근해야 html파일로 정상적인 접근이 가능하다.&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;단순 정적 페이지에 접근하려면 /src/main/resources/static 디렉토리에 html파일을 생성해야한다.&lt;/p&gt;
&lt;p&gt;(static 디렉토리도 만들어야한다)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3번--&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;결제를 하는 메인 로직이다.&lt;/p&gt;
&lt;p&gt;ajax를 통해 서버로 결제 결과를 보낸다. (만약 서버로 보낼 정보가 딱히 없다면 안써도 된다)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;url은 내가 결과를 보낼 api이다.&lt;/p&gt;
&lt;p&gt;주의해야할 점은 AJAX의 CORS(cross-domain)오류에 주의해야한다.&lt;/p&gt;
&lt;p&gt;ajax는 처음해봐서 조금 해맸다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;CORS를 방지하려면&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;현재 클라이언트(html파일)의 도메인과 서버의 도메인이 같아야한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 클라이언트 접근 url이 &lt;u&gt;&lt;b&gt;http://abcdef/kakaoPayment.html?price=2000&lt;/b&gt;&lt;/u&gt;이라면&lt;/p&gt;
&lt;p&gt;서버 rest api url이 &lt;u&gt;&lt;b&gt;http://abcdef/payments/complete&lt;/b&gt;&lt;/u&gt; 여야한다.&lt;/p&gt;
&lt;p&gt;CORS를 해결하는 방법이 별도로 존재하는 것 같다.&lt;/p&gt;
&lt;p&gt;여러 검색을 해본 결과 개발자들이 엄청 싫어하는 오류인 것 같닼ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ&lt;/p&gt;
&lt;p&gt;지긋지긋하다는 글을 제법 많이 봤다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 나는 이를 맞춰주려고 서버 코드에 클라이언트 페이지를 넣었던 것이다(/src/main/resources/static)&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;100%맞는 것을 확신할 수 없지만 이후에 ajax에 대해 더 공부를 해봐야 제대로 알 것 같다. &amp;zwj; &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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후에 Springboot api(/payments/complete)에 들어오는 Json을 parsing해서 db에 저장까지 완료하였다.&lt;/p&gt;
&lt;p&gt;어려운 문제가 아닌 사소하고 쉬운 오류들을 직면했기에 마음이 아팠다 &lt;/p&gt;
&lt;p&gt;하지만 해결하고 DB저장이 잘되는 것을 확인하니 짜릿하다 ㅎㅎㅎ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대단한 기능같지만 구현은 쉬운 카카오 페이 결제 연동 과정이었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아임포트측에서 아주 자세한 레퍼런스들을 많이 제공한다.&lt;/p&gt;
&lt;p&gt;이를 잘 보는것도 중요하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Engineering/Server</category>
      <category>AJAX</category>
      <category>Controller</category>
      <category>IOS</category>
      <category>java</category>
      <category>restapi</category>
      <category>restcontroller</category>
      <category>springboot</category>
      <category>가상 결제</category>
      <category>아임포트</category>
      <category>카카오페이</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/58</guid>
      <comments>https://hello-gg.tistory.com/58#entry58comment</comments>
      <pubDate>Thu, 25 Feb 2021 01:46:50 +0900</pubDate>
    </item>
    <item>
      <title>백준[1244]- 스위치 켜고 끄기</title>
      <link>https://hello-gg.tistory.com/57</link>
      <description>&lt;p&gt;진짜 문제는 끝까지 읽어야 한다.&lt;/p&gt;
&lt;p&gt;쉬운 문제였지만 출력 조건을 제대로 안읽어서 자꾸 출력 오류가 났었다....&lt;/p&gt;
&lt;p&gt;반성반성&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제 그대로 구현만하면 되는 쉬운문제다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;557&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/mduSf/btqYtNWxbnd/RZ65CoSaEUR2lXhhKXeTmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mduSf/btqYtNWxbnd/RZ65CoSaEUR2lXhhKXeTmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mduSf/btqYtNWxbnd/RZ65CoSaEUR2lXhhKXeTmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmduSf%2FbtqYtNWxbnd%2FRZ65CoSaEUR2lXhhKXeTmk%2Fimg.png&quot; width=&quot;557&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;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;549&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VCsop/btqYn4dVVEB/iQhpyTz98ecLKundKNvzeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VCsop/btqYn4dVVEB/iQhpyTz98ecLKundKNvzeK/img.png&quot; data-alt=&quot;https://www.acmicpc.net/problem/1244&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VCsop/btqYn4dVVEB/iQhpyTz98ecLKundKNvzeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVCsop%2FbtqYn4dVVEB%2FiQhpyTz98ecLKundKNvzeK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;549&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;https://www.acmicpc.net/problem/1244&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1614182095494&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
        private static int[] arr;
   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int n = Integer.parseInt(sc.nextLine());

       String inputStr = sc.nextLine();
       String[] inputArr = (inputStr).split(&quot; &quot;);

       arr = new int[n+1];
       arr[0]=0;
       for(int i=0;i&amp;lt;n;i++){
           arr[i + 1] = Integer.parseInt(inputArr[i]);
       }

       int stun = Integer.parseInt(sc.nextLine());
       for(int l=0;l&amp;lt;stun;l++){
           String input = sc.nextLine();

           String[] inputarr = input.split(&quot; &quot;);
           int num= Integer.parseInt(inputarr[1]);

           if(inputarr[0].equals(&quot;1&quot;)) {//남자
               if(num&amp;gt;n)
                   continue;
               for(int i=num;i&amp;lt;=n;i+=num){
                    int temp = arr[i];
                    arr[i]=(temp+1)%2;
               }

           }else{//여자
               int midtemp = arr[num];
               arr[num]=(midtemp+1)%2;
               int start = num;
               int last = num;
               while(start-1 != 0 &amp;amp;&amp;amp; last+1 !=arr.length){
                   start--;
                   last++;
                   if(arr[start]!=arr[last]){
                       break;
                   }
                   int starttemp=arr[start];
                   int lasttemp= arr[last];
                   arr[start] = (starttemp+1)%2;
                   arr[last] = (lasttemp+1)%2;
               }

           }
       }


       for(int i=1;i&amp;lt;arr.length;i++){
           System.out.print(arr[i]+&quot; &quot;);
            if(i%20==0)
                System.out.println();
       }


   }



}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;dfs,bfs공부한 이후로 배열의 index주는 방식이 바뀌었다.&lt;/p&gt;
&lt;p&gt;예전에는 꾸역꾸역 index 0부터 채워넣었지만,&lt;/p&gt;
&lt;p&gt;이제는 유연하게 1부터 값을 채워넣는 방법을 쓴다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;훨씬 덜 헷갈린다!!&lt;/p&gt;</description>
      <category>Computer Engineering/알고리즘 테스트</category>
      <category>java</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>코테</category>
      <category>형변환</category>
      <author>말하는호구마</author>
      <guid isPermaLink="true">https://hello-gg.tistory.com/57</guid>
      <comments>https://hello-gg.tistory.com/57#entry57comment</comments>
      <pubDate>Thu, 25 Feb 2021 00:56:43 +0900</pubDate>
    </item>
  </channel>
</rss>