<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>기억하긴 버거워</title>
    <link>https://dev-alxndr.tistory.com/</link>
    <description>기억하긴 버거워서 적기 시작하는 블로그입니다. 휘발성메모리</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 21:59:09 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Alxndr</managingEditor>
    <image>
      <title>기억하긴 버거워</title>
      <url>https://tistory1.daumcdn.net/tistory/3947442/attach/2ad6e62e569547deb6631ab081ca59b3</url>
      <link>https://dev-alxndr.tistory.com</link>
    </image>
    <item>
      <title>블로그 이사했습니다.</title>
      <link>https://dev-alxndr.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev-alxndr.github.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;새로운 깃헙 블로그&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672062777708&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;Alexander Choi&quot; data-og-description=&quot;Dev Blog by Junior Backend Developer.&quot; data-og-host=&quot;dev-alxndr.github.io&quot; data-og-source-url=&quot;https://dev-alxndr.github.io/&quot; data-og-url=&quot;https://dev-alxndr.github.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/daUM57/hyQ32xKREj/X4XypvVqYcmfTF89KYqwok/img.jpg?width=400&amp;amp;height=400&amp;amp;face=184_94_270_188&quot;&gt;&lt;a href=&quot;https://dev-alxndr.github.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev-alxndr.github.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/daUM57/hyQ32xKREj/X4XypvVqYcmfTF89KYqwok/img.jpg?width=400&amp;amp;height=400&amp;amp;face=184_94_270_188');&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;Alexander Choi&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Dev Blog by Junior Backend Developer.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev-alxndr.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Markdown 형식으로 작성 바로 커밋 푸시하여 관리할 수 있는 깃헙 블로그로 이사를 하였습니다.&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;</description>
      <category>Story</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/47</guid>
      <comments>https://dev-alxndr.tistory.com/47#entry47comment</comments>
      <pubDate>Mon, 26 Dec 2022 22:55:39 +0900</pubDate>
    </item>
    <item>
      <title>Why Kafka is fast?</title>
      <link>https://dev-alxndr.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;유튜브를 보던 중 추천 영상으로 떠서 보게 되어 간단하게 정리해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카가 빠르다는 의미는 무엇일까?&lt;/p&gt;
&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqbF63/btrN5E9iqVD/3FNfy5f36ozaw6VzgjoY80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqbF63/btrN5E9iqVD/3FNfy5f36ozaw6VzgjoY80/img.png&quot; data-alt=&quot;직경이 큰 파이프에 액체가 많이 이동한다고 생각해보세요.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqbF63/btrN5E9iqVD/3FNfy5f36ozaw6VzgjoY80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqbF63%2FbtrN5E9iqVD%2F3FNfy5f36ozaw6VzgjoY80%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;602&quot; height=&quot;266&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;750&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;그래서 누군가 카프카가 빠르다고 말하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로, 많은 데이터를 보내는 카프카의 성능을 말하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_22.54.15.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhHXiK/btrN4ECaxoS/FTqXe6MQU7i6EPFOB2tROK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhHXiK/btrN4ECaxoS/FTqXe6MQU7i6EPFOB2tROK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhHXiK/btrN4ECaxoS/FTqXe6MQU7i6EPFOB2tROK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhHXiK%2FbtrN4ECaxoS%2FFTqXe6MQU7i6EPFOB2tROK%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;502&quot; height=&quot;309&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_22.54.15.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 카프카는 Sequential I/O입니다.&lt;/h3&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;1. Random Access Pattern&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리에 랜덤 하게 접근하는 방법&lt;/li&gt;
&lt;li&gt;물리적으로 다른 위치로 이동합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.03.58.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mCwxl/btrN5aHpxbR/3CHaFOj7SSPMcpDfUUkBA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mCwxl/btrN5aHpxbR/3CHaFOj7SSPMcpDfUUkBA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mCwxl/btrN5aHpxbR/3CHaFOj7SSPMcpDfUUkBA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmCwxl%2FbtrN5aHpxbR%2F3CHaFOj7SSPMcpDfUUkBA0%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;296&quot; height=&quot;1112&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.03.58.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;1112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Sequential Access Pattern&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리에 순차적으로 접근하는 방법&lt;/li&gt;
&lt;li&gt;바로 다음 위치로 이동하여 Random Access Pattern 보다 빠릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.04.51.png&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiTcNg/btrN4anOWR2/NhMPocgwFfcN9p1L7TsNW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiTcNg/btrN4anOWR2/NhMPocgwFfcN9p1L7TsNW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiTcNg/btrN4anOWR2/NhMPocgwFfcN9p1L7TsNW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeiTcNg%2FbtrN4anOWR2%2FNhMPocgwFfcN9p1L7TsNW1%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;305&quot; height=&quot;233&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.04.51.png&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tech.kakao.com/2016/07/17/coding-for-ssd-part-5/&quot;&gt;개발자를 위한 SSD (Coding for SSD) - Part 5 : 접근 방법과 시스템 최적화&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카는 Append-only log를 사용하여 Sequential Access Pattern의 이점을 가졌습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.07.04.png&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;962&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnIYuK/btrN4OdulF4/k0yR1tlKlgLIvODXp0IGl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnIYuK/btrN4OdulF4/k0yR1tlKlgLIvODXp0IGl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnIYuK/btrN4OdulF4/k0yR1tlKlgLIvODXp0IGl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnIYuK%2FbtrN4OdulF4%2Fk0yR1tlKlgLIvODXp0IGl1%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;345&quot; height=&quot;329&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.07.04.png&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;962&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Zero Copy Principle (복사하지 않는다!)&lt;/h3&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;1. without Zero Copy&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.13.50.png&quot; data-origin-width=&quot;2302&quot; data-origin-height=&quot;1202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ypEHn/btrN4kD0Bsc/lYTrhao9yyxIjKOKbc8zYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ypEHn/btrN4kD0Bsc/lYTrhao9yyxIjKOKbc8zYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ypEHn/btrN4kD0Bsc/lYTrhao9yyxIjKOKbc8zYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FypEHn%2FbtrN4kD0Bsc%2FlYTrhao9yyxIjKOKbc8zYk%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;661&quot; height=&quot;345&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.13.50.png&quot; data-origin-width=&quot;2302&quot; data-origin-height=&quot;1202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PrincipleOS 레벨에서 4번의 복사와 2번의 시스템 호출이 일어납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. with Zero Copy&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.15.09.png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;1134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1c2Ys/btrN5jxzw9j/AK3jhGn0FoFIHu6oDvAiV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1c2Ys/btrN5jxzw9j/AK3jhGn0FoFIHu6oDvAiV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1c2Ys/btrN5jxzw9j/AK3jhGn0FoFIHu6oDvAiV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1c2Ys%2FbtrN5jxzw9j%2FAK3jhGn0FoFIHu6oDvAiV1%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;608&quot; height=&quot;360&quot; data-filename=&quot;Screen_Shot_2022-07-30_at_23.15.09.png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;1134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Principle 첫 번째 Disk로부터 OS Buffer로 데이터를 로드하는 과정은 같습니다. 이런 방법은 OS Cache에서 네트워크 카드 Buffer로 단 한 번의 복사만 이러납니다. DMA : Direct Memory access&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;현대의 네트워크 카드는 DMA로 데이터를 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zero Copy는 카프카 애플리케이션은 &lt;code&gt;sendfile()이라는&lt;/code&gt; 시스템 호출을 사용하여 OS에게 곧바로 NIC Buffer로 데이터를 복사할 수 있습니다&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;위 두 가지 Sequential I/O 와 Zero Copy Principle가 카프카의 높은 성능에 기초가 됩니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;References&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=UNUz1-msbOM&quot;&gt;https://www.youtube.com/watch?v=UNUz1-msbOM&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%8A%A4%EB%A3%A8%ED%92%8B&quot;&gt;스루풋 - 위키백과, 우리 모두의 백과사전&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%A7%81%EC%A0%91_%EB%A9%94%EB%AA%A8%EB%A6%AC_%EC%A0%91%EA%B7%BC&quot;&gt;직접 메모리 접근 - 위키백과, 우리 모두의 백과사전&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Zero-copy&quot;&gt;Zero-copy - Wikipedia&lt;/a&gt;&lt;/p&gt;</description>
      <category>Dev/Server</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/46</guid>
      <comments>https://dev-alxndr.tistory.com/46#entry46comment</comments>
      <pubDate>Sat, 8 Oct 2022 23:22:10 +0900</pubDate>
    </item>
    <item>
      <title>동시성 이슈를 해결하는 여러가지 방법</title>
      <link>https://dev-alxndr.tistory.com/45</link>
      <description>&lt;h1&gt;재고시스템으로 알아보는 동시성 이슈 해결방법&lt;/h1&gt;
&lt;p&gt;해당 글은 &lt;a href=&quot;https://www.inflearn.com/course/%EB%8F%99%EC%8B%9C%EC%84%B1%EC%9D%B4%EC%8A%88-%EC%9E%AC%EA%B3%A0%EC%8B%9C%EC%8A%A4%ED%85%9C#&quot;&gt;인프런 | 재고시스템으로 알아보는 동시성 이슈 해결방법&lt;/a&gt; 강의를 보고 정리한 자료입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;모든 코드는 &lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock&quot;&gt;Github&lt;/a&gt;에 공개되어 있으며 각 해결 단계를 커밋으로 분리해두었으니 확인해주세요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;상품이 판매될 때마다 재고를 하나씩 줄여주는 기능을 만들어본다.&lt;br&gt;이 때 발생할 수 있는 동시성 문제를 하나씩 해결해보자.&lt;/p&gt;
&lt;h2&gt;문제발생 케이스&lt;/h2&gt;
&lt;p&gt;하나의 상품이 100개의 재고를 가지고 있을 때 단순하게 생각하면 판매될 때 재고를 -1 한 후 저장해주면 될거라고 생각할 수 있지만, 실무에서는 다양한 케이스가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;대표적으로,&lt;br&gt;하나의 상품에 주문이 동시에 (ms 차이 수준)  들어온다고 가정을 한다.&lt;br&gt;이럴 때, Race Condition이 발생하여 최종 재고 값이 이상하게 나올 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock/commit/f2dfb8782f81936fcb00241bd10babc29dd6913a&quot;&gt;문제의 상황 테스트 코드&lt;/a&gt;&lt;br&gt;위 예시에서 100갸의 쓰레드가 같은 상품의 재고를 감소시켰을 때 최종 재고는 0을 기대하지만 실제 테스트는 실패하는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;각 단계별로 해결을 하며 문제점을 살펴봅니다.&lt;/p&gt;
&lt;h2&gt;1. Syncronized (level 1)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ktko.tistory.com/entry/%EC%9E%90%EB%B0%94-synchronized%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC&quot;&gt;자바-synchronized에-대하여&lt;/a&gt;&lt;br&gt;Java의 기본 Syncronized 키워드를 사용해서 여러 쓰레드가 동시에 해당 접근할 수 없도록 한다.&lt;br&gt;&lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock/commit/fec64269cc22862800d451d6ff6b0784623e425d&quot;&gt;Syncronized를 활용한 개선 커밋&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;결과&lt;/h3&gt;
&lt;p&gt;예시처럼 코드를 작성하면 쓰레드 동기화를 통해 해결했다고 생각할 수 있지만, 결과는 실패한다.&lt;br&gt;이유는 &lt;code&gt;@Transactiional&lt;/code&gt; Proxy 방식 때문에 Proxy가 (Commit)종료되기 전에&lt;br&gt;다른 쓰레드가 해당 자원에 접근할 수 있기 때문이다.&lt;br&gt;&lt;a href=&quot;https://jiwondev.tistory.com/154#head11&quot;&gt;@Transactional 동작원리&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;해결법&lt;/h3&gt;
&lt;p&gt;서비스에 붙어있는 &lt;code&gt;@Transactional&lt;/code&gt; 을 없앤다..&lt;br&gt;&lt;code&gt;@Transactional&lt;/code&gt;을 제거하면 Proxy로 동작하지 않기 떄문에 해결 할 수는 있다.&lt;/p&gt;
&lt;h3&gt;문제점&lt;/h3&gt;
&lt;p&gt;Syncronized는 하나의 프로세스에서만 동기화를 보장한다.&lt;br&gt;현대의 어플리케이션은 2대 이상의 서버를 사용하기 때문에 Syncronized 로는 보장할 수 없다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. Mysql 을 활용한 다양한 방법 (level 2)&lt;/h2&gt;
&lt;h3&gt;Pessimistic Lock을 활용&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock/commit/87d819820b22e17768861139d9fa605b7ed5468a&quot;&gt;Pessimistic Lock Commit&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실제 데이터에 Lock을 걸어서 정합성을 맞추는 방법&lt;/li&gt;
&lt;li&gt;Exclusive Lock을 걸게되면 다른 트랜잭션에서는 Lock이 해제되기 전에는 데이터를 가져갈 수 없음&lt;/li&gt;
&lt;li&gt;Dead Lock 가능성이 있기 때문에 주의필요&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select stock0_.id as id1_0_, stock0_.product_id as product_2_0_, stock0_.quantity as quantity3_0_ from stock stock0_ where stock0_.id=? for update&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;select..for update&lt;/code&gt; 를 사용하여 해당 row를 다른 세션에서 건들 수 없도록 Lock을 건다.&lt;/p&gt;
&lt;h5&gt;장점&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;충돌이 빈번하게 일어나면 Optimistic Lock보다 성능이 좋을 수 있음&lt;/li&gt;
&lt;li&gt;Lock을 잡기 때문에 데이터 정합성을 지킬 수 있음&lt;h5&gt;단점&lt;/h5&gt;
&lt;/li&gt;
&lt;li&gt;성능감소&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optimistic Lock&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock/commit/c2d0288553ec926f5afb65d64fc6fbc65d3ba897&quot;&gt;Optimistic Lock Commit&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실제 Lock을 이용하지 않고 버전을 이용함으로써 정합성을 맞추는 방법&lt;/li&gt;
&lt;li&gt;먼저 데이터를 읽은 후 update를 수행할 때 현재 내가 읽은 버전이 맞는지 확인하여 업데이트&lt;/li&gt;
&lt;li&gt;내가 읽은 버전에서 수정사항이 생겼을 경우 application 단에서 다시 읽은 후 작업을 수행해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;장점&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;별도의 Lock이 없기 때문에 성능상 이점&lt;h5&gt;단점&lt;/h5&gt;
&lt;/li&gt;
&lt;li&gt;실패했을 시 로직을 개발자가 직접 만들어야함&lt;/li&gt;
&lt;li&gt;충돌이 빈번히 일어나면 성능상 손해&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt; &lt;a href=&quot;https://sabarada.tistory.com/175&quot;&gt;Pessimistic Lock vs Optimistic Lock&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;Named Lock&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock/commit/39c1d089b4c19218b19fcf3610a6d7c109aba3a1&quot;&gt;Named Lock Commit&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이름을 가진 Metadata Locking (Table/Row Lock 이 아님)&lt;/li&gt;
&lt;li&gt;이름을 가진 Lock을 획득한 후 해제할 때 까지 다른 세션은 이 Lock 을 획득할 수 없도록 합니다.&lt;/li&gt;
&lt;li&gt;Transaction이 종료될 때 자동으로 해제되지 않음. 별도의 명령어로 해제/선점 시간이 끝나야 해제됨&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;JPA Native Query를 이용&lt;br&gt;Connction Pool 이 부족해질 수 있기 때문에 별도의 Datasource를 이용하기를 추천&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;참고 자료&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_exclusive_lock&quot;&gt;exclusive_lock&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html&quot;&gt;innodb-locking&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html&quot;&gt;locking-functions&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Redis 활용 (level 3)&lt;/h2&gt;
&lt;h3&gt;1. Lettuce&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/dev-alxndr/concurrency-stock/commit/99755f933bc2e4fe73e6539145905192f2defe5a&quot;&gt;Redis Lettuce Commit&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;setnx&lt;/code&gt; 명령어 사용&lt;/li&gt;
&lt;li&gt;spin lock 방식 &lt;ul&gt;
&lt;li&gt;retry 로직 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;장점&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;구현이 간단하다&lt;/li&gt;
&lt;li&gt;spring data redis 를 이용하면 lettuce 가 기본이기때문에 별도의 라이브러리를 사용하지 않아도 된다.    &lt;h5&gt;단점&lt;/h5&gt;
&lt;/li&gt;
&lt;li&gt;spin lock 방식이기때문에 동시에 많은 스레드가 lock 획득 대기 상태라면 redis 에 부하가 갈 수 있다.&lt;ul&gt;
&lt;li&gt;Thread.sleep으로 부하를 줄여줬음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Redisson&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Pub-Sub 기반의 Lock 구현 제공&lt;/li&gt;
&lt;li&gt;채널을 만들고 Lock을 획득한 Thread가 Lock획득을 시도하는 Thread에게 해제되었음을 알려주는 방식&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnccTu/btrN4xQEpJl/F9OaqYR4CchuZIh8lyqk61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnccTu/btrN4xQEpJl/F9OaqYR4CchuZIh8lyqk61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnccTu/btrN4xQEpJl/F9OaqYR4CchuZIh8lyqk61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnccTu%2FbtrN4xQEpJl%2FF9OaqYR4CchuZIh8lyqk61%2Fimg.png&quot; width=&quot;100%&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;h5&gt;장점&lt;/h5&gt;
&lt;/li&gt;
&lt;li&gt;락 획득 재시도를 기본으로 제공한다.&lt;/li&gt;
&lt;li&gt;pub-sub 방식으로 구현이 되어있기 때문에 lettuce 와 비교했을 때 redis 에 부하가 덜 간다.&lt;h5&gt;단점&lt;/h5&gt;
&lt;/li&gt;
&lt;li&gt;별도의 라이브러리를 사용해야한다.&lt;/li&gt;
&lt;li&gt;lock 을 라이브러리 차원에서 제공해주기 떄문에 사용법을 공부해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;실무에서는 ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;실패 시 재시도가 필요하지 않은 lock 은 lettuce 활용&lt;/li&gt;
&lt;li&gt;실패 시 재시도가 필요한 경우에는 redisson 를 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mysql vs Redis&lt;/h2&gt;
&lt;p&gt;Mysql&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이미 Mysql 을 사용하고 있다면 별도의 비용없이 사용가능하다.    &lt;/li&gt;
&lt;li&gt;어느정도의 트래픽까지는 문제없이 활용이 가능하다&lt;/li&gt;
&lt;li&gt;Redis 보다는 성능이 좋지않다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Redis&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mysql 보다 성능이 좋다.&lt;/li&gt;
&lt;li&gt;활용중인 Redis 가 없다면 별도의 구축비용과 인프라 관리비용이 발생한다.    &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@hgs-study/redisson-distributed-lock&quot;&gt;https://velog.io/@hgs-study/redisson-distributed-lock&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://techblog.woowahan.com/2631/&quot;&gt;https://techblog.woowahan.com/2631/&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://kwonnam.pe.kr/wiki/database/mysql/user_lock&quot;&gt;https://kwonnam.pe.kr/wiki/database/mysql/user_lock&lt;/a&gt;&lt;/p&gt;</description>
      <category>Dev/Spring</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/45</guid>
      <comments>https://dev-alxndr.tistory.com/45#entry45comment</comments>
      <pubDate>Sat, 8 Oct 2022 23:10:11 +0900</pubDate>
    </item>
    <item>
      <title>[Fast Api] 3. Query Parameter</title>
      <link>https://dev-alxndr.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameters&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Path Variable이 아닌 다른 함수 매개변수를 선언하면, 쿼리 매개변수로 자동 해석됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter란 URL에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;?&lt;span&gt;&amp;nbsp;&lt;/span&gt;후에 나오는 값을 말합니다.&lt;br /&gt;&amp;amp;로 구분되는 키-값 쌍의 집합&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.exmaple.com/?size=10&amp;amp;page=1&quot;&gt;www.exmaple.com?size=10&amp;amp;page=1&lt;/a&gt;&lt;br /&gt;size : 10/&lt;span&gt;&amp;nbsp;&lt;/span&gt;page : 1&lt;span&gt;&amp;nbsp;&lt;/span&gt;값입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre id=&quot;code_1623490501407&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 임시 데이터
mock_items_db = [
    {&quot;item_name&quot;: &quot;Foo&quot;},
    {&quot;item_name&quot;: &quot;Bar&quot;},
    {&quot;item_name&quot;: &quot;Baz&quot;}
]

@app.get(&quot;/items&quot;)
async def read_items(skip: int = 0, limit: int = 10):
    return mock_items_db[skip : skip + limit]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Path Variable과 마찬가지로 타입 지정이 가능하며, 기본값을 지정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;span&gt;skip&lt;/span&gt;: &lt;span&gt;int&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;0&lt;/span&gt;, &lt;span&gt;limit&lt;/span&gt;: &lt;span&gt;int&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;10&lt;/span&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-null-허용&quot; href=&quot;https://github.com/alxndrDev/fast-api-example/blob/master/docs/3_query_param.md#null-%ED%97%88%EC%9A%A9&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;Null 허용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QueryParameter&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 선택적으로 선언할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1623490526766&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Optional

@app.get(&quot;/items/{item_id}&quot;)
async def get_item(item_id : str, q: Optional[str] = None):
    if q:
        return {&quot;item_id&quot;: item_id, &quot;q&quot;: q}
    return {&quot;item_id&quot;: item_id}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fast Api는&lt;span&gt;&amp;nbsp;&lt;/span&gt;q = None이라는걸 선택적이라 인지합니다.&lt;br /&gt;Optional은 Fast Api가 사용하는건 아니지만, 코드에서 오류를 찾아낼 수 있게 도와줍니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;q를 보내지 않은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;354&quot; width=&quot;311&quot; height=&quot;275&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVuXK0/btq64LJV0nJ/GDWm7lTkztX92As6wqkzYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVuXK0/btq64LJV0nJ/GDWm7lTkztX92As6wqkzYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVuXK0/btq64LJV0nJ/GDWm7lTkztX92As6wqkzYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVuXK0%2Fbtq64LJV0nJ%2FGDWm7lTkztX92As6wqkzYK%2Fimg.png&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;354&quot; width=&quot;311&quot; height=&quot;275&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;q를 보낸 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;365&quot; width=&quot;294&quot; height=&quot;307&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejHw2U/btq67SA7K5Z/HzDo2WGXsvgIOrinshgKJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejHw2U/btq67SA7K5Z/HzDo2WGXsvgIOrinshgKJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejHw2U/btq67SA7K5Z/HzDo2WGXsvgIOrinshgKJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejHw2U%2Fbtq67SA7K5Z%2FHzDo2WGXsvgIOrinshgKJ1%2Fimg.png&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;365&quot; width=&quot;294&quot; height=&quot;307&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-boolean-type&quot; href=&quot;https://github.com/alxndrDev/fast-api-example/blob/master/docs/3_query_param.md#boolean-type&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;Boolean Type&lt;/h2&gt;
&lt;pre id=&quot;code_1623490550946&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
async def get_item(item_id: str, q: Optional[str] = None, short: bool = False):
    item = {&quot;item_id&quot;: item_id}
    if q:
        item.update({&quot;q&quot;: q})
    if not short:
        item.update(
            {&quot;description&quot;: &quot;LOOOOOOONG Description.&quot;}
        )
    return item
    &lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Result&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;421&quot; width=&quot;288&quot; height=&quot;306&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZmAgj/btq69WQLN9n/HMeJArUxDbkdN9CUXTFKj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZmAgj/btq69WQLN9n/HMeJArUxDbkdN9CUXTFKj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZmAgj/btq69WQLN9n/HMeJArUxDbkdN9CUXTFKj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZmAgj%2Fbtq69WQLN9n%2FHMeJArUxDbkdN9CUXTFKj1%2Fimg.png&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;421&quot; width=&quot;288&quot; height=&quot;306&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;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 아래 사진처럼 소개하는데 True 뜻을 가진 값은 True로 인식한다?&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;992&quot; width=&quot;367&quot; height=&quot;284&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8dfco/btq65eSEEUh/9dc6YWzOTIaVAqhiH070y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8dfco/btq65eSEEUh/9dc6YWzOTIaVAqhiH070y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8dfco/btq65eSEEUh/9dc6YWzOTIaVAqhiH070y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8dfco%2Fbtq65eSEEUh%2F9dc6YWzOTIaVAqhiH070y0%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;992&quot; width=&quot;367&quot; height=&quot;284&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;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-여러-경로--쿼리-매개변수&quot; href=&quot;https://github.com/alxndrDev/fast-api-example/blob/master/docs/3_query_param.md#%EC%97%AC%EB%9F%AC-%EA%B2%BD%EB%A1%9C--%EC%BF%BC%EB%A6%AC-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;여러 경로 &amp;amp; 쿼리 매개변수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러경로와 쿼리 매개변수를 동시에 선언해도 순서를 신경쓸 필요가 없습니다. Fast Api는 변수의 이름으로 감지합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1623490573385&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/users/{user_id}/items/{item_id}&quot;)
async def read_user_item(
    user_id: int, item_id: str, q: Optional[str] = None, short: bool = False
):
    item = {&quot;item_id&quot;: item_id, &quot;owner_id&quot;: user_id}
    if q:
        item.update({&quot;q&quot;: q})
    if not short:
        item.update(
            {&quot;description&quot;: &quot;This is an amazing item&quot;}
        )
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-정리&quot; href=&quot;https://github.com/alxndrDev/fast-api-example/blob/master/docs/3_query_param.md#%EC%A0%95%EB%A6%AC&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter&lt;span&gt;&amp;nbsp;&lt;/span&gt;조건별&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 48.0233%; height: 90px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;조건&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;방법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;require&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;item_id: str&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;nullable&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;item_id: str = None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;default value&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;size: int = 10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;이번 포스팅은 여기서 마무리하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/alxndrDev/fast-api-example&quot;&gt;Github&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;틀린점과 개선할 점은 댓글로 알려주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>Dev/Python</category>
      <category>API</category>
      <category>FastAPI</category>
      <category>Python</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/41</guid>
      <comments>https://dev-alxndr.tistory.com/41#entry41comment</comments>
      <pubDate>Sat, 12 Jun 2021 18:37:10 +0900</pubDate>
    </item>
    <item>
      <title>[Fast Api] 2. Path Variable 사용해서 핸들러 매핑하기</title>
      <link>https://dev-alxndr.tistory.com/40</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 사용법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI
app = FastAPI()


@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id):
    return {&quot;item_id&quot;: item_id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;{item_id}&lt;/code&gt;에 매핑되어 &lt;code&gt;read_item(item_id)&lt;/code&gt; 매개변수로 받을 수 있습니다.&lt;br /&gt;서버를 실행 한 후 PostMan으로 위에서 만든 핸들러를 호출해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;357&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRdeSS/btq6V337AQh/tHEwFgjVyEFZB5xCW9bveK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRdeSS/btq6V337AQh/tHEwFgjVyEFZB5xCW9bveK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRdeSS/btq6V337AQh/tHEwFgjVyEFZB5xCW9bveK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRdeSS%2Fbtq6V337AQh%2FtHEwFgjVyEFZB5xCW9bveK%2Fimg.png&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;357&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;{item_id}로 받은 값이 출력되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;매개변수 타입지정&lt;/h2&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id: int):
    return {&quot;item_id&quot;: item_id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;: int&lt;/code&gt;로 지정해주면 됩니다.&lt;br /&gt;만약 &lt;code&gt;/items/3&lt;/code&gt;을 호출하면 &quot;3&quot;이 아닌 int형 3으로 받게됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 str 값을 넘길 경우 에러가 발생합니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;487&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cigqjO/btq6RINZoKw/lJXxNld6rvh5wRHtVzwlT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cigqjO/btq6RINZoKw/lJXxNld6rvh5wRHtVzwlT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cigqjO/btq6RINZoKw/lJXxNld6rvh5wRHtVzwlT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcigqjO%2Fbtq6RINZoKw%2FlJXxNld6rvh5wRHtVzwlT1%2Fimg.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;487&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 data-ke-size=&quot;size16&quot;&gt;파이썬 타입을 선언하면 데이터 검증을 진행하며, 오류는 검증을 통과하지 못한 지점까지 정확하게 명시한다고 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Enum 타입 매개변수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정된 Enum 타입으로 매개변수를 받고 싶다면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from enum import Enum
from fastapi import FastAPI

app = FastAPI()

class Brand(str, Enum):
    nike = &quot;nike&quot;
    adidas = &quot;adidas&quot;

@app.get(&quot;/brands/{brand_name}&quot;)
async def get_brand(brand_name: Brand):
    if brand_name == Brand.nike:
        return {&quot;brand_name&quot;: brand_name, &quot;message&quot;: &quot;JUST DO IT.&quot;}

    if brand_name == Brand.adidas:
        return {&quot;brand_name&quot;: brand_name, &quot;message&quot;: &quot;IMPOSSIBLE IS NOTHING.&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;358&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pztw3/btq6SxkTce7/fTIuWpaI6kxD1eOjcNJSi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pztw3/btq6SxkTce7/fTIuWpaI6kxD1eOjcNJSi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pztw3/btq6SxkTce7/fTIuWpaI6kxD1eOjcNJSi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpztw3%2Fbtq6SxkTce7%2FfTIuWpaI6kxD1eOjcNJSi0%2Fimg.png&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;358&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;Brand&amp;nbsp;타입으로 지정을 했기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록되지 않은 Enum일 경우에러와 함께 등록된 Enum 목록을 알려줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;579&quot; width=&quot;491&quot; height=&quot;414&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbP15d/btq6WvsxPit/HQkpPivray1rlxMQFXuuo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbP15d/btq6WvsxPit/HQkpPivray1rlxMQFXuuo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbP15d/btq6WvsxPit/HQkpPivray1rlxMQFXuuo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbP15d%2Fbtq6WvsxPit%2FHQkpPivray1rlxMQFXuuo0%2Fimg.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;579&quot; width=&quot;491&quot; height=&quot;414&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 여기서 마무리하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/alxndrDev/fast-api-example&quot;&gt;Github&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;틀린점과 개선할 점은 댓글로 알려주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>Dev/Python</category>
      <category>FastAPI</category>
      <category>Python</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/40</guid>
      <comments>https://dev-alxndr.tistory.com/40#entry40comment</comments>
      <pubDate>Wed, 9 Jun 2021 22:25:24 +0900</pubDate>
    </item>
    <item>
      <title>[Fast Api] 1. 처음 시작하기</title>
      <link>https://dev-alxndr.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Facebook 을 보다&lt;a href=&quot;https://jybaek.tistory.com/890&quot;&gt;FastAPI 톺아보기 - 부제: python 백엔드 봄은 온다&lt;/a&gt; 이러한 글을 보게되었는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굉장히 간결하고 좋아보여서 한번 공부해보려합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;369&quot; width=&quot;408&quot; height=&quot;147&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV1MnE/btq6V4hFrTZ/1d1dkXvhBKOVmpac6bAtM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV1MnE/btq6V4hFrTZ/1d1dkXvhBKOVmpac6bAtM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV1MnE/btq6V4hFrTZ/1d1dkXvhBKOVmpac6bAtM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbV1MnE%2Fbtq6V4hFrTZ%2F1d1dkXvhBKOVmpac6bAtM0%2Fimg.png&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;369&quot; width=&quot;408&quot; height=&quot;147&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/&quot;&gt;Fast Api&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;공식 사이트에선 Fast Api 특징을 아래처럼 소개하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;590&quot; width=&quot;591&quot; height=&quot;272&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vQ5Ag/btq6WuAnZrv/csxOzj5c2TLHop1pyq2plK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vQ5Ag/btq6WuAnZrv/csxOzj5c2TLHop1pyq2plK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vQ5Ag/btq6WuAnZrv/csxOzj5c2TLHop1pyq2plK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvQ5Ag%2Fbtq6WuAnZrv%2FcsxOzj5c2TLHop1pyq2plK%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;590&quot; width=&quot;591&quot; height=&quot;272&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python version : 3.6+&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Fast Api 패키지 설치&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bash&lt;br /&gt;
&lt;pre id=&quot;code_1623245689721&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$pip install fastapi[all]

# 만약 zsh: no matches found: fastapi[all] 오류가 난다면
# 아래로 설치해주세요.
$pip install 'fastapi[all]'&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://github.com/tiangolo/fastapi/issues/1069&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;no matches found: error&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Python Project &amp;amp; main.py 생성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1623244159133&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI # 1

app = FastAPI() # 2

@app.get(&quot;/&quot;) # 3
async def root(): # 4
    return {&quot;message&quot; : &quot;Hello Fast Api&quot;} # 5&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FastApi 임포트&lt;/li&gt;
&lt;li&gt;Instance 생성&lt;/li&gt;
&lt;li&gt;루트 핸들러 생성
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Request Methods&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@app.post()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.put()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.delete()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.options()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.head()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.patch()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.trace()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;핸들러가 실행할 함수&lt;/li&gt;
&lt;li&gt;컨텐츠 반환
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dict,liststr,int등 json으로 자동 변환되는 객체들과 모델들이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Start Server&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bash&lt;br /&gt;$uvicorn main:app --reload
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;--reload: 코드 변경시 자동 재시작 (개발에서만 사용)&amp;nbsp;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;108&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BzwPG/btq6SwGhHDx/9K0fZDS1r4pYFwU0qZiADK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BzwPG/btq6SwGhHDx/9K0fZDS1r4pYFwU0qZiADK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BzwPG/btq6SwGhHDx/9K0fZDS1r4pYFwU0qZiADK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBzwPG%2Fbtq6SwGhHDx%2F9K0fZDS1r4pYFwU0qZiADK%2Fimg.png&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;108&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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;br /&gt;&lt;a href=&quot;https://www.uvicorn.org/&quot;&gt;About Uvicorn&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://blog.neonkid.xyz/249&quot;&gt;About ASGI&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 만든 API 호출해보기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;368&quot; width=&quot;407&quot; height=&quot;205&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLhWkB/btq6VJLphTz/iVJdKd5jLBKwWPVMoFx9N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLhWkB/btq6VJLphTz/iVJdKd5jLBKwWPVMoFx9N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLhWkB/btq6VJLphTz/iVJdKd5jLBKwWPVMoFx9N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLhWkB%2Fbtq6VJLphTz%2FiVJdKd5jLBKwWPVMoFx9N1%2Fimg.png&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;368&quot; width=&quot;407&quot; height=&quot;205&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;h3 data-ke-size=&quot;size23&quot;&gt;5. API Docs 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fast API는 API 문서를 자동으로 만들어 줍니다.&lt;a href=&quot;http://localhost:8000/docs%EB%A1%9C&quot;&gt;http://localhost:8000/docs로&lt;/a&gt; 접속해서 확인해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;1534&quot; width=&quot;314&quot; height=&quot;390&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G7qa5/btq6RNnWKg3/uRvv3wn5BAmf8UuGoAb2Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G7qa5/btq6RNnWKg3/uRvv3wn5BAmf8UuGoAb2Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G7qa5/btq6RNnWKg3/uRvv3wn5BAmf8UuGoAb2Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG7qa5%2Fbtq6RNnWKg3%2FuRvv3wn5BAmf8UuGoAb2Uk%2Fimg.png&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;1534&quot; width=&quot;314&quot; height=&quot;390&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;여기에서 API 목록과 결과 예시를 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://localhost:8000/redoc%EB%8B%A4%EB%A5%B8&quot;&gt;http://localhost:8000/redoc&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;아주 간단하고 빠르게 API 서버를 만들어봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 재밌어서 좀 더 공부해서 앞으로도 공유하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코드는 &lt;a href=&quot;https://github.com/alxndrDev/fast-api-example&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github&lt;/a&gt; 에서 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;틀린점과 개선할 점은 댓글로 알려주시면 감사하겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reference&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/tutorial/&quot;&gt;Fast Api Tutorial&lt;/a&gt;&lt;/p&gt;</description>
      <category>Dev/Python</category>
      <category>FastAPI</category>
      <category>Python</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/39</guid>
      <comments>https://dev-alxndr.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 9 Jun 2021 22:12:40 +0900</pubDate>
    </item>
    <item>
      <title>MapStruct 를 사용하여 Entity Dto 쉽게 변환하기</title>
      <link>https://dev-alxndr.tistory.com/38</link>
      <description>&lt;h1&gt;MapStruct&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소개&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO&amp;lt;-&amp;gt;Entity간 객체 Mapping을 편하게 도와주는 라이브러리&lt;br /&gt;비슷한 라이브러리로&lt;a href=&quot;http://modelmapper.org/&quot;&gt;ModelMapper&lt;/a&gt;가 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ModelMapper는 변환과정에서 리플렉션이 발생합니다.&lt;br /&gt;MapStruct는 컴파일 시점에 구현체를 만들어내기 떄문에 리플렉션이 발생하지 않아 보다 빠릅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 Gradle 프로젝트로 구성&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. add dependency&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;plugins {
    id 'java'
}

ext {
    mapstructVersion = '1.4.2.Final'
    lombokVersion = &quot;1.18.12&quot;
}

group 'io.alxndr'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    // 순서 주의!
    // Lombok 사용시 MapStruct가 먼저오게 작성해야함
    implementation &quot;org.mapstruct:mapstruct:${mapstructVersion}&quot;
    compileOnly 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
    compileOnly group: 'org.projectlombok', name: 'lombok', version: &quot;${lombokVersion}&quot;
    annotationProcessor &quot;org.projectlombok:lombok:${lombokVersion}&quot;
    annotationProcessor &quot;org.mapstruct:mapstruct-processor:${mapstructVersion}&quot;

    // If you are using mapstruct in test code
    // testAnnotationProcessor &quot;org.mapstruct:mapstruct-processor:1.4.2&quot;

    testCompile group: 'junit', name: 'junit', version: '4.12'
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Create Test Dto and Model&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Person.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;//@Entity
@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    private Long id;

    private String name;

    private String email;

    private String birth;

    private String group;

    private LocalDateTime creationDate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PersonDto.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PersonDto {
    private Long id;

    private String name;

    private String email;

    private String birth;

    private String team;

}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person은 Entity 역할을한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Mapper 정의&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Mapper
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    /**
     * @return {@link Person}
     */
    @Mapping(target = &quot;id&quot;, ignore = true)  // 1 
    @Mapping(source = &quot;team&quot;, target = &quot;group&quot;) // 2
    @Mapping(target = &quot;creationDate&quot;, expression = &quot;java(java.time.LocalDateTime.now())&quot;)   // 3
    Person toEntity(PersonDto personDto);

    @Mapping(source = &quot;group&quot;, target = &quot;team&quot;) // 4
    PersonDto toDto(Person person);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;toEntity(): Dto =&amp;gt; Entity로 변환&lt;/li&gt;
&lt;li&gt;toDto(): Entity =&amp;gt; Dto변환&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DTO에서Entity로 변환시 ID는 제외&lt;/li&gt;
&lt;li&gt;DTO.team을Entity.group으로 매핑&lt;/li&gt;
&lt;li&gt;Entity.creationDate는LocalDateTime.now()로 초기화&lt;/li&gt;
&lt;li&gt;Entity.group은DTO.team매핑&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapper를 만든 후 Project Build를 한번 해준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;279&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bft18G/btq44OWFbiw/abgg3PBKqE1t5WPUyaKN11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bft18G/btq44OWFbiw/abgg3PBKqE1t5WPUyaKN11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bft18G/btq44OWFbiw/abgg3PBKqE1t5WPUyaKN11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbft18G%2Fbtq44OWFbiw%2Fabgg3PBKqE1t5WPUyaKN11%2Fimg.png&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;279&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;544&quot; data-origin-height=&quot;789&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwYGxK/btq5azjBlTK/8XJUFH0QSiKYv7x2yQPFB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwYGxK/btq5azjBlTK/8XJUFH0QSiKYv7x2yQPFB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwYGxK/btq5azjBlTK/8XJUFH0QSiKYv7x2yQPFB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwYGxK%2Fbtq5azjBlTK%2F8XJUFH0QSiKYv7x2yQPFB1%2Fimg.png&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;789&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 처럼 MapperImpl이 생긴것을 확인할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. TEST&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PersonTest.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public class PersonTest {

    @Test
    public void dtoToEntity() {
        PersonDto personDto = PersonDto.builder()
                .id(1L)
                .name(&quot;Alexander Choi&quot;)
                .email(&quot;dev.alxndr@gmail.com&quot;)
                .birth(&quot;1995-01-01&quot;)
                .build();

        Person person = PersonMapper.INSTANCE.toEntity(personDto);

        assertEquals(personDto.getEmail(), person.getEmail());
        assertEquals(personDto.getTeam(), person.getGroup());
    }

    @Test
    public void entityToDto() {
        Person person = Person.builder()
                .id(1L)
                .name(&quot;Alexander Choi&quot;)
                .email(&quot;dev.alxndr@gmail.com&quot;)
                .birth(&quot;1995-01-01&quot;)
                .build();

        PersonDto personDto = PersonMapper.INSTANCE.toDto(person);

        assertEquals(personDto.getEmail(), person.getEmail());
        assertEquals(personDto.getTeam(), person.getGroup());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체코드는 아래 깃헙에서 확인하실 수 있습니다.&lt;br /&gt;&lt;a href=&quot;https://github.com/alxndrDev/Study/tree/master/demo-mapstruct&quot;&gt;깃헙!&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;References&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mapstruct.org/documentation/stable/reference/html/&quot;&gt;Reference Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huisam.tistory.com/entry/mapStruct&quot;&gt;huisam Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://meetup.toast.com/posts/213&quot;&gt;Meet up Toast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangchhe.github.io/spring/2021/01/25/ModelMapperAndMapStruct/&quot;&gt;ModelMapper vs MapStruct&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Dev/Java</category>
      <category>Java</category>
      <category>mapstruct</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/38</guid>
      <comments>https://dev-alxndr.tistory.com/38#entry38comment</comments>
      <pubDate>Tue, 18 May 2021 10:50:13 +0900</pubDate>
    </item>
    <item>
      <title>[security] 4. PasswordEncoder 설정하기</title>
      <link>https://dev-alxndr.tistory.com/37</link>
      <description>&lt;h1&gt;PasswordEncoder 설정하기&lt;/h1&gt;
&lt;p&gt;기존의&lt;span&gt;&amp;nbsp;&lt;/span&gt;{noop}&lt;span&gt;&amp;nbsp;&lt;/span&gt;문자열을 더해줘서 Password encoding 하던 것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;Spring Security가 지원해주는&lt;span&gt;&amp;nbsp;&lt;/span&gt;PasswordEncoder를 사용해보겠습니다.&lt;/p&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-bean-등록&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#bean-%EB%93%B1%EB%A1%9D&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;Bean 등록&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Application.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706998565&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
public class Application {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    } // 1

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;PasswordEncoder를 Bean를 등록해줍니다.&lt;/li&gt;
&lt;li&gt;우리가 선택한&lt;span&gt;&amp;nbsp;&lt;/span&gt;NoOpPasswordEncoder는 Spring Framework 5이전에 사용되던 방식입니다.&lt;/li&gt;
&lt;li&gt;현재는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Deprecated된 상태입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-등록한-bean-주입받아서-유저-등록-시-encoding하기&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#%EB%93%B1%EB%A1%9D%ED%95%9C-bean-%EC%A3%BC%EC%9E%85%EB%B0%9B%EC%95%84%EC%84%9C-%EC%9C%A0%EC%A0%80-%EB%93%B1%EB%A1%9D-%EC%8B%9C-encoding%ED%95%98%EA%B8%B0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;등록한 Bean 주입받아서 유저 등록 시 encoding하기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;AccountService.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620707016199&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 AccountService implements UserDetailsService {
    ... 생략
    private final PasswordEncoder passwordEncoder;  // 1

    public Account createAccount(Account account) {
        account.setPassword(passwordEncoder.encode(account.getPassword())); // 2
        return accountRepository.save(account);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Bean으로 등록한&lt;span&gt;&amp;nbsp;&lt;/span&gt;PasswordEncoder를&lt;span&gt;&amp;nbsp;&lt;/span&gt;AccountService에서를 생성자주입방식으로 주입받습니다.&lt;/li&gt;
&lt;li&gt;{noop}문자열을 더해주던 부분에 주입받은&lt;span&gt;&amp;nbsp;&lt;/span&gt;PasswordEncoder를 사용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;encode해줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-확인&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#%ED%99%95%EC%9D%B8&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;확인&lt;/h3&gt;
&lt;p&gt;서버를 구동하여 지난 번글과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;http://localhost:8080/account/USER/alxndr/1234&lt;span&gt;&amp;nbsp;&lt;/span&gt;유저를 등록해보면&lt;span&gt;&amp;nbsp;&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/Kpuyk/btq4Ed8UqKY/MTMmf398dZWtSBnXk6ahXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kpuyk/btq4Ed8UqKY/MTMmf398dZWtSBnXk6ahXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kpuyk/btq4Ed8UqKY/MTMmf398dZWtSBnXk6ahXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKpuyk%2Fbtq4Ed8UqKY%2FMTMmf398dZWtSBnXk6ahXK%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;br /&gt;그림과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;{noop}은 붙어있지않은 패스워드가 저장되게 됩니다.&lt;/p&gt;
&lt;p&gt;그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;/dashboard로 접속하여 로그인해보면 정상적으로 로그인되는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-왜-spring-5에서는-특이한-포멧이-생겼는가&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#%EC%99%9C-spring-5%EC%97%90%EC%84%9C%EB%8A%94-%ED%8A%B9%EC%9D%B4%ED%95%9C-%ED%8F%AC%EB%A9%A7%EC%9D%B4-%EC%83%9D%EA%B2%BC%EB%8A%94%EA%B0%80&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;왜 Spring 5에서는 특이한 포멧이 생겼는가?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/site/docs/current/reference/html5/#authentication-password-storage-history&quot;&gt;패스워드인코더의역사..참고&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;기본 전략이 Bcrypt로 변경되었습니다.
&lt;ul&gt;
&lt;li&gt;만약 이전 Spring 4에서 패스워드를 평문으로 저장하고 있었다면, 버전을 5로 올리게되면 인증이 꺠지게 됩니다.&lt;/li&gt;
&lt;li&gt;여러 프로젝트들은 여러가지 암호화 알고리즘을 사용하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt; 이러한 이유로&lt;span&gt;&amp;nbsp;&lt;/span&gt;{ID}~~라는 포멧이 생겨나게 됐습니다.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-passwordencoder-변경&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#passwordencoder-%EB%B3%80%EA%B2%BD&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;PasswordEncoder 변경&lt;/h2&gt;
&lt;p&gt;등록한 Bean을 수정해주겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Application.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620707032540&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
public class Application {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();  // 1
    }
    ... 생략
}@SpringBootApplication public class Application { @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); // 1 } ... 생략 }&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;PasswordEncoderFactories에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;createDelegatingPasswordEncoder()를 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;createDelegatingPasswordEncoder&lt;span&gt;&amp;nbsp;&lt;/span&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/cjMYFi/btq4Bxm46R4/sZsFZwEaL1RISd3tkxD27k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjMYFi/btq4Bxm46R4/sZsFZwEaL1RISd3tkxD27k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjMYFi/btq4Bxm46R4/sZsFZwEaL1RISd3tkxD27k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjMYFi%2Fbtq4Bxm46R4%2FsZsFZwEaL1RISd3tkxD27k%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;span&gt;&amp;nbsp;&lt;/span&gt;같이 다양한 Encoder를 가지고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-확인-1&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#%ED%99%95%EC%9D%B8-1&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;확인&lt;/h2&gt;
&lt;p&gt;다시한번 서버를 재구동 후 유저를 만들어보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-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/biokuE/btq4CjWmk7y/ngmMsC3au2xqzXZ7WOvOJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biokuE/btq4CjWmk7y/ngmMsC3au2xqzXZ7WOvOJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biokuE/btq4CjWmk7y/ngmMsC3au2xqzXZ7WOvOJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiokuE%2Fbtq4CjWmk7y%2FngmMsC3au2xqzXZ7WOvOJ0%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;span&gt;&amp;nbsp;&lt;/span&gt;그림과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;bcrypt로 암호화되어 저장된것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-강의-번외&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#%EA%B0%95%EC%9D%98-%EB%B2%88%EC%99%B8&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;강의 번외&lt;/h2&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/bQ78Uw/btq4CN31A9i/tUxFYpycWTLzvIKOKylC6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ78Uw/btq4CN31A9i/tUxFYpycWTLzvIKOKylC6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ78Uw/btq4CN31A9i/tUxFYpycWTLzvIKOKylC6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ78Uw%2Fbtq4CN31A9i%2FtUxFYpycWTLzvIKOKylC6k%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;span&gt;&amp;nbsp;&lt;/span&gt;사진과 같이 저장한 비밀번호와&lt;span&gt;&amp;nbsp;&lt;/span&gt;1234가 같은 패스워드인지 확인하는 과정을 보겠습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;우선&lt;span&gt;&amp;nbsp;&lt;/span&gt;PasswordEncoderFactories는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Map으로 된 Encoder 묶음?을 리턴해줍니다.&lt;span&gt;&amp;nbsp;&lt;/span&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/brhBxp/btq4AuRnmJj/PFjhBj4agfxKj9ruyj8kJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brhBxp/btq4AuRnmJj/PFjhBj4agfxKj9ruyj8kJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brhBxp/btq4AuRnmJj/PFjhBj4agfxKj9ruyj8kJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrhBxp%2Fbtq4AuRnmJj%2FPFjhBj4agfxKj9ruyj8kJ1%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;/li&gt;
&lt;li&gt;PasswordEncoder의 실제 구현체는&lt;span&gt;&amp;nbsp;&lt;/span&gt;DelegatingPasswordEncoder를 사용하게 되고 그 안에&lt;span&gt;&amp;nbsp;&lt;/span&gt;matches()를 확인해보겠습니다.&lt;span&gt;&amp;nbsp;&lt;/span&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/qrNet/btq4ErTGgQZ/Z0JH6VlRhlfuaRbpcBKnSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qrNet/btq4ErTGgQZ/Z0JH6VlRhlfuaRbpcBKnSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qrNet/btq4ErTGgQZ/Z0JH6VlRhlfuaRbpcBKnSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqrNet%2Fbtq4ErTGgQZ%2FZ0JH6VlRhlfuaRbpcBKnSk%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;ol&gt;
&lt;li&gt;rawPassword와 DB에 저장된 암호화된 패스워드를 인자로 받는다.&lt;/li&gt;
&lt;li&gt;extractId(prefixEncodedPassword)를 통해 암호화된 패스워드가 어떤 알고리즘인지 확인한다.&lt;/li&gt;
&lt;li&gt;this.idToPasswordEncoder&lt;span&gt;&amp;nbsp;&lt;/span&gt;Map에서 id로 해당 PasswordEncoder를 찾아온다.&lt;/li&gt;
&lt;li&gt;extractEcodedPassword()로&lt;span&gt;&amp;nbsp;&lt;/span&gt;{ID}외의 패스워드를 추출한다.&lt;/li&gt;
&lt;li&gt;3번에서 찾아온&lt;span&gt;&amp;nbsp;&lt;/span&gt;PasswordEncoder가 만약&lt;span&gt;&amp;nbsp;&lt;/span&gt;BcryptPasswordEncoder라고 한다면 해당 클래스에&lt;span&gt;&amp;nbsp;&lt;/span&gt;matches()를 이용하여 패스워드가 일치하는지 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-정리&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/04_passwordEncoder.md#%EC%A0%95%EB%A6%AC&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;Spring Security 에서 PasswordEncoder를 사용하는 방법과 비밀번호 일치여부를 확인하는 과정을 알아봤습니다.&lt;/p&gt;
&lt;p&gt;틀린점과 개선할 상항이 있다면 알려주세요. 감사합니다.&lt;/p&gt;</description>
      <category>Dev/Spring</category>
      <category>Security</category>
      <category>Spring</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/37</guid>
      <comments>https://dev-alxndr.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 11 May 2021 13:24:14 +0900</pubDate>
    </item>
    <item>
      <title>[security] 3. JPA와 Security 연동</title>
      <link>https://dev-alxndr.tistory.com/36</link>
      <description>&lt;h1&gt;JPA와 Security 연동&lt;/h1&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-이전의-문제점&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#%EC%9D%B4%EC%A0%84%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;이전의 문제점&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;매번 유저를 추가하는 일이 생길 경우 코드를 수정해야 한다.&lt;/li&gt;
&lt;li&gt;수정, 삭제도 마찬가지로 코드를 수정해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt; 위와 같은 문제를 DB를 연동하여 유저 정보를 관리할 수 있도록 수정해보겠습니다. (JPA를 사용하겠습니다.)&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-개선&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#%EA%B0%9C%EC%84%A0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;개선&lt;/h2&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-jpa-설정&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#jpa-%EC%84%A4%EC%A0%95&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;JPA 설정&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;user-content-dependency-추가&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#dependency-%EC%B6%94%EA%B0%80&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;dependency 추가&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706375046&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
...
    // JPA
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    // H2
    runtimeOnly 'com.h2database:h2'
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a id=&quot;user-content-class-생성&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#class-%EC%83%9D%EC%84%B1&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;class 생성&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/wWzes/btq4z0JNZRU/4cga2t9RQ58aIfgh34WuTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wWzes/btq4z0JNZRU/4cga2t9RQ58aIfgh34WuTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wWzes/btq4z0JNZRU/4cga2t9RQ58aIfgh34WuTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwWzes%2Fbtq4z0JNZRU%2F4cga2t9RQ58aIfgh34WuTk%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;ul&gt;
&lt;li&gt;Account&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706872704&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter
@Setter
public class Account {

    @Id @GeneratedValue
    private Long id;
    @Column(unique = true)
    private String username;
    private String password;
    private String role;

}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;AccountRepository&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706892978&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface AccountRepository extends JpaRepository&amp;lt;Account, Long&amp;gt; {
    Account findByUsername(String username);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;AccountService&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706905916&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 AccountService implements UserDetailsService {

    private final AccountRepository accountRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Account account = accountRepository.findByUsername(username);
        if (account == null) {
            throw new UsernameNotFoundException(username);  // 1
        }

        return User.builder()   // 2
                .username(account.getUsername())
                .password(account.getPassword())
                .roles(account.getRole())
                .build();
    }

    public Account createAccount(Account account) {
        return accountRepository.save(account);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;해당 Username 으로 찾는 유저 정보가 없는 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;UsernameNotFoundException을 터트리도록 합니다.&lt;/li&gt;
&lt;li&gt;loadUserByUsername()은&lt;span&gt;&amp;nbsp;&lt;/span&gt;UserDeatils를 리턴하는데 우리가 가진 유저객체는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Account이므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;UserDetails를 구현해둔&lt;span&gt;&amp;nbsp;&lt;/span&gt;User를 빌드하여 리턴한다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a id=&quot;user-content-테스트를-위한-회원등록-controller&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%9A%8C%EC%9B%90%EB%93%B1%EB%A1%9D-controller&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;테스트를 위한 회원등록 Controller&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AccountController&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706918860&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequiredArgsConstructor
public class AccountController {
    private final AccountService accountService;

    @GetMapping(&quot;/account/{role}/{username}/{password}&quot;)
    public Account createAccount(@ModelAttribute Account account) {
        return accountService.createAccount(account);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;실제로 회원가입기능을 이런식으로 만들면 안됩니다 간단한 테스트용입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;프로젝트 재시동 후&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;http://localhost:8080/account/USER/alxndr/1234&lt;/li&gt;
&lt;li&gt;http://localhost:8080/account/ADMIN/admin/1234&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출하여 유저 정보를 등록해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그 후&lt;span&gt;&amp;nbsp;&lt;/span&gt;/dashboard&lt;span&gt;&amp;nbsp;&lt;/span&gt;에 접근하여 방금 등록한 정보로 로그인해봅니다.&lt;/p&gt;
&lt;p&gt;그러면 에러가 발생하게 되는데&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;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/ci27Fj/btq4CjPu9Hu/tdkjQTHveAKFE5NLjjoV8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci27Fj/btq4CjPu9Hu/tdkjQTHveAKFE5NLjjoV8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci27Fj/btq4CjPu9Hu/tdkjQTHveAKFE5NLjjoV8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci27Fj%2Fbtq4CjPu9Hu%2FtdkjQTHveAKFE5NLjjoV8k%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;span&gt;&amp;nbsp;&lt;/span&gt;스프링 시큐리티는 특정한 패스워드 패턴을 요구하는데 ({noop}1234) 현재 저장된 password는&lt;span&gt;&amp;nbsp;&lt;/span&gt;123으로 저장되어 있기 때문에 발생하는 에러입니다.&lt;/p&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-에러-개선&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#%EC%97%90%EB%9F%AC-%EA%B0%9C%EC%84%A0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;에러 개선&lt;/h3&gt;
&lt;p&gt;우선은 패스워드를 스프링 시큐리티가 원하는 규격으로 만들어주겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AccountService&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706934815&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class AccountService implements UserDetailsService {
    ... 생략    
    public Account createAccount(Account account) {
        account.setPassword(&quot;{noop}&quot; + account.getPassword());  // 1
        return accountRepository.save(account);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Inmemory user를 저장할 떄 처럼 패스워드 앞에&lt;span&gt;&amp;nbsp;&lt;/span&gt;{noop}을 붙여서 저장을 해주도록 하겠습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;프로젝트를 재시동 후 다시 한번&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;http://localhost:8080/account/USER/alxndr/1234&lt;/li&gt;
&lt;li&gt;http://localhost:8080/account/ADMIN/admin/1234&lt;br /&gt;회원을 등록해주면 아래와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;{noop}YOUR-PASSWORD로 저장되고&lt;br /&gt;/dashboard에 접근하여 로그인해보면 정상적으로 동작하는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t9DEj/btq4HvuoCKw/XyhWSPosMKCaMXp8MRrv81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t9DEj/btq4HvuoCKw/XyhWSPosMKCaMXp8MRrv81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t9DEj/btq4HvuoCKw/XyhWSPosMKCaMXp8MRrv81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft9DEj%2Fbtq4HvuoCKw%2FXyhWSPosMKCaMXp8MRrv81%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;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/NFXhm/btq4EsLJVjk/AYgl9998yIk2NpuZFUzK00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NFXhm/btq4EsLJVjk/AYgl9998yIk2NpuZFUzK00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NFXhm/btq4EsLJVjk/AYgl9998yIk2NpuZFUzK00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNFXhm%2Fbtq4EsLJVjk%2FAYgl9998yIk2NpuZFUzK00%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;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/FmOD9/btq4CDNIfEt/rvrszzkSijLZRxu5XkKwT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FmOD9/btq4CDNIfEt/rvrszzkSijLZRxu5XkKwT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FmOD9/btq4CDNIfEt/rvrszzkSijLZRxu5XkKwT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFmOD9%2Fbtq4CDNIfEt%2FrvrszzkSijLZRxu5XkKwT0%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;h3&gt;&lt;a id=&quot;user-content-명시적으로-userdetailsservice-지정하기&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#%EB%AA%85%EC%8B%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-userdetailsservice-%EC%A7%80%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;명시적으로 UserDetailsService 지정하기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SecurityConfig&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1620706949460&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    private final AccountService accountService;
    ....생략
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(accountService);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;AuthenticationManagerBuilder로 명시적으로 지정해줄 수 있지만, 하지 않아도&lt;span&gt;&amp;nbsp;&lt;/span&gt;UserDeatilsService를 구현한 구현체가 Bean으로 등록만 되어있다면 사용에 문제는 없습니다.&lt;/p&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-마무리&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/03_JPA%EC%99%80%20Security%EC%97%B0%EB%8F%99.md#%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;마무리&lt;/h3&gt;
&lt;p&gt;Inmemory User의 불편한 점들을 JPA와의 연동으로 해결해봤습니다. 다음으로는 임시로 적어준&lt;span&gt;&amp;nbsp;&lt;/span&gt;{noop}을 스프링 시큐리티가 제공해주는&lt;span&gt;&amp;nbsp;&lt;/span&gt;PasswordEncoder&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 활용하여 개선해보겠습니다.&lt;/p&gt;</description>
      <category>Dev/Spring</category>
      <category>Security</category>
      <category>Spring</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/36</guid>
      <comments>https://dev-alxndr.tistory.com/36#entry36comment</comments>
      <pubDate>Tue, 11 May 2021 13:22:46 +0900</pubDate>
    </item>
    <item>
      <title>[security] 2. Inmemory User</title>
      <link>https://dev-alxndr.tistory.com/35</link>
      <description>&lt;h1&gt;Inmemory User&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;현재 기본으로 생성되는 유저와 패스워드&lt;span&gt;&amp;nbsp;&lt;/span&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/7Li91/btq4F6aA3Z7/BptP1zDqNuKd6ZLqOeZfW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Li91/btq4F6aA3Z7/BptP1zDqNuKd6ZLqOeZfW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Li91/btq4F6aA3Z7/BptP1zDqNuKd6ZLqOeZfW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Li91%2Fbtq4F6aA3Z7%2FBptP1zDqNuKd6ZLqOeZfW0%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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;log를 확인해보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;UserDeatilsServiceAutoConfiguration에서 찍히는걸 확인할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UserDetailsServiceAutoConfiguration.java&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/bSnzOM/btq4EtcOjj4/6uRK1pxRYVWJFFuYYoCvck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSnzOM/btq4EtcOjj4/6uRK1pxRYVWJFFuYYoCvck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSnzOM/btq4EtcOjj4/6uRK1pxRYVWJFFuYYoCvck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSnzOM%2Fbtq4EtcOjj4%2F6uRK1pxRYVWJFFuYYoCvck%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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;User&lt;span&gt;&amp;nbsp;&lt;/span&gt;정보는&lt;span&gt;&amp;nbsp;&lt;/span&gt;SecurityProperties에서 가져오며&lt;span&gt;&amp;nbsp;&lt;/span&gt;name,&lt;span&gt;&amp;nbsp;&lt;/span&gt;password,&lt;span&gt;&amp;nbsp;&lt;/span&gt;role을 설정 할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;yml 수정하여 기본 생성되는 유저 정보 변경하기&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&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/k0GQo/btq4AtERHMx/VyZkjkyVVqgzaALWzzi8iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k0GQo/btq4AtERHMx/VyZkjkyVVqgzaALWzzi8iK/img.png&quot; data-alt=&quot;application.yml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k0GQo/btq4AtERHMx/VyZkjkyVVqgzaALWzzi8iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk0GQo%2Fbtq4AtERHMx%2FVyZkjkyVVqgzaALWzzi8iK%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;application.yml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;http://localhost:8080/login&lt;span&gt;&amp;nbsp;&lt;/span&gt;에 접속하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;properties에 작성한 ID, Password를 입력해주면 기존처럼 접속이 가능합니다..&lt;/p&gt;
&lt;h3&gt;&lt;a id=&quot;user-content-문제점&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/02_inmemory_user.md#%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;문제점&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;유저 정보가 1개밖에 없다.&lt;/li&gt;
&lt;li&gt;소스를 보면 어떤 유저정보가 있는지 확인할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt; 그러므로 유저정보를 properties로 설정하지 않겠습니다.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-개선&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/02_inmemory_user.md#%EA%B0%9C%EC%84%A0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;개선&lt;/h2&gt;
&lt;p&gt;Security Configration&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일에서 유저 정보를 추가해주겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1620706270878&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //...
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser(&quot;alxndr&quot;).password(&quot;{noop}1234&quot;).roles(&quot;USER&quot;).and()
                .withUser(&quot;admin&quot;).password(&quot;{noop}4321&quot;).roles(&quot;ADMIN&quot;);   // {PREFIX}password
    }
    //...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{noop}은 PasswordEncoder 에게 어떠한 형식으로 암호화되었는지 알려주는 Prefix입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;{bcypt},&lt;span&gt;&amp;nbsp;&lt;/span&gt;{sha256}등이 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;user-content-확인&quot; href=&quot;https://github.com/alxndrDev/Study/blob/master/inflearn-spring-security/howTo/02_inmemory_user.md#%ED%99%95%EC%9D%B8&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;확인&lt;/h2&gt;
&lt;p&gt;프로젝트를 재시동 후&lt;span&gt;&amp;nbsp;&lt;/span&gt;localhost:8080/login으로 접속하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;alxndr&lt;span&gt;&amp;nbsp;&lt;/span&gt;계정으로 로그인 후&lt;span&gt;&amp;nbsp;&lt;/span&gt;/info를 접속해보면 잘 되는 것을 확인 할 수 있으며,&lt;br /&gt;/admin을 접속할 시&lt;span&gt;&amp;nbsp;&lt;/span&gt;alxndr&lt;span&gt;&amp;nbsp;&lt;/span&gt;계정은&lt;span&gt;&amp;nbsp;&lt;/span&gt;USER&lt;span&gt;&amp;nbsp;&lt;/span&gt;권한만 가지고 있으므로 접근이 안되는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;admin&lt;span&gt;&amp;nbsp;&lt;/span&gt;계정으로 접속하면 모두 접근 할 수 있는 것도 확인 할 수 있습니다.&lt;/p&gt;</description>
      <category>Dev/Spring</category>
      <category>Security</category>
      <category>Spring</category>
      <author>Alxndr</author>
      <guid isPermaLink="true">https://dev-alxndr.tistory.com/35</guid>
      <comments>https://dev-alxndr.tistory.com/35#entry35comment</comments>
      <pubDate>Tue, 11 May 2021 13:12:17 +0900</pubDate>
    </item>
  </channel>
</rss>