<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Lake_Light</title>
    <link>https://lakelight.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 6 Jul 2026 00:14:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>lakelight</managingEditor>
    <image>
      <title>Lake_Light</title>
      <url>https://tistory1.daumcdn.net/tistory/5442119/attach/2488e012853540138cc476d324df3458</url>
      <link>https://lakelight.tistory.com</link>
    </image>
    <item>
      <title>[Elasticsearch] Elasticsearch란?</title>
      <link>https://lakelight.tistory.com/140</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLlE8y/btrXaFfaBjU/FTKMECcHezUqBKTB3262Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLlE8y/btrXaFfaBjU/FTKMECcHezUqBKTB3262Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLlE8y/btrXaFfaBjU/FTKMECcHezUqBKTB3262Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLlE8y%2FbtrXaFfaBjU%2FFTKMECcHezUqBKTB3262Kk%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;394&quot; height=&quot;205&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Elasticsearch&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Apache Lucene 기반의 Java 오픈소스 분산형 Restful 검색 및 분석 엔진입니다.&lt;br /&gt;방대한 양의 데이터에 대해 실시간으로 저장과 검색 및 분석 등의 작업을 수행할 수 있습니다.&lt;br /&gt;&lt;br /&gt;Elasticsearch는 정형 데이터, 비정형 데이터, 지리 데이터 등 모든 타입의 데이터를 처리할 수 있는데,&amp;nbsp; &lt;br /&gt;이를 JSON문서로 데이터를 저장할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Elasticsearch와 RDB 비교&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 50.6977%; height: 392px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Relational Database&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;ElasticSearch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Database&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Table&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Row&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Column&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Index&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Analyze&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;primary Key&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Schema&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Physical partition&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Shard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Logical partition&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Relational&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Parent/Child, Nested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;SQL&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Query DSL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Elasticsearch는 데이터를 행렬 데이터로 저장하는 것이 아니라, JSON 문서로 직렬화된 복잡한 자료 구조를 저장하는 방식을 채택하고 있습니다. 따라서 기존 RDB에서 사용하던 용어를 그대로 사용하지 않습니다. 위에 표에 정리하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Elasticsearch는 특정 문장을 입력받으면, 파싱을 통해 문장을 단어 단위로 분리하여 저장합니다. 또한 대분자를 소문자로 치환하거나 유사어 체크 등의 추가 작업을 통해 텍스트를 저장합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Elasticsearch는 '역 색인'이라고 하는 자료 구조를 사용하는데, 이는 전문 검색에 있어서 빠른 성능을 보장합니다. 책의 전반부에 위치한 일반적인 목차가 Index라면, 책 후반부에 키워드마다 내용을 찾아볼 수 있도록 돕는 목차가 Reverted Index입니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;역 색인은 Document에 등장하는 모든 고유한 단어들을 리스트업하고, 해당 단어들이 등장하는 Document들을 식별합니다. 색인은 최적화된 Document컬렉션이며, 각 Document는 데이터를 포함하고 있는 Key-Value 쌍으로 이루어진 Field의 컬렉션입니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Elasticsearch는 모든 Field의 데이터를 인덱싱하는데, 인덱싱된 Field는 각각의 최적화된 자료구조를 사용합니다. 텍스트 형식의 Field는 Inverted Index에 저장되며, 숫자 혹은 지리 관련 Field는 BKD 트리에 저장됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;역인덱스 데이터 저장 구조와 검색&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDB는 특정 단어 검색 할때 Row 개수 만큼 확인을 해야하지만, 단어 기반으로 데이터를 저장하는 Elasticsearch는 특정 단어가 어디에 저장되어 있는지 이미 알고 있어 모든 Document를 검색할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 수정과 삭제 할 때는 내부적으로 많은 리소스가 소요되어, RDBMS가 더 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Elasticsearch 구조&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wdNTC/btrXVx7JgUQ/TwjkSWZuzkMQ6BYdcsoJB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wdNTC/btrXVx7JgUQ/TwjkSWZuzkMQ6BYdcsoJB1/img.png&quot; data-alt=&quot;이미지 출처:&amp;amp;nbsp;https://www.mitrais.com/news-updates/elasticsearch-101/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wdNTC/btrXVx7JgUQ/TwjkSWZuzkMQ6BYdcsoJB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwdNTC%2FbtrXVx7JgUQ%2FTwjkSWZuzkMQ6BYdcsoJB1%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;590&quot; height=&quot;273&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처:&amp;nbsp;https://www.mitrais.com/news-updates/elasticsearch-101/&lt;/figcaption&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;Cluster
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;하나 이상의 노드로 이루어진 노드들의 집합&lt;/li&gt;
&lt;li&gt;클러스터는 각각 독립적인 시스템을 유지 (다른 클러스터의 데이터 접근 교환 불가)&lt;/li&gt;
&lt;li&gt;여러 대의 서버가 하나의 클러스터를 구성하거나, 하나의 서버에 여러 개의 클러스터가 존재 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Node
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ElasticSearch를 구성하는 하나의 단위 프로세스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Shard&lt;br /&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;Sacle-Out을 위해 RDB의 Database에 해당하는 Index를 여러 Sahrd로 나눔&lt;/li&gt;
&lt;li&gt;기본적으로 1개가 존재하고, 검색 성능 향상을 위해 클러스터의 Shard 개수를 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Replica
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 형태의 Shard로 노드를 손실 했을 경우, 데이터 신뢰성을 위해 Shard 복제&lt;/li&gt;
&lt;li&gt;서로 다른 노드에 위치시킬 것을 권장ㄱ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;노드 종류&lt;/h4&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;li&gt;색인된 데이터 CRUD 역할&lt;/li&gt;
&lt;li&gt;메타 데이터 등 전체 클러스터를 제어하는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Elasticsearch 특징&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Scale out - Shard를 통해 규모가 수평적으로 늘어날 수 있습니다.&lt;/li&gt;
&lt;li&gt;고가용성 - Replica를 통해 데이터의 안정성을 보장하고, 단일 장애점을 극복합니다.&lt;/li&gt;
&lt;li&gt;Schema Free - Json 문서를 통해 데이터를 검색하므로, 스키마의 개념이 없습니다.&lt;/li&gt;
&lt;li&gt;RESTful - CRUD 작업은 RESTful API를 통해 수행되며, 각각이 HTTP의 PUT/GET/POST/DELETE 메서드에 대응됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이번 시간에는 Elasitcsearch에 대해서 알아보겠습니다.&lt;br /&gt;언젠가는 대규모 처리를 할 수 있는 날을 대비해 공부를 하였습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다음시간에는 Spring Data Elasticsearch를 구현해보겠습니다.&lt;br /&gt;감사합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-10-19-elasticsearch/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Data Elasticsearch 설정 및 검색 기능 구현&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BigDataProcess/Elasticsearch</category>
      <category>elasticsearch</category>
      <category>lakelight</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/140</guid>
      <comments>https://lakelight.tistory.com/140#entry140comment</comments>
      <pubDate>Fri, 3 Feb 2023 10:08:39 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커란 무엇인가?</title>
      <link>https://lakelight.tistory.com/139</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker 도커&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ocUyg/btrXfMDrnFG/ukTqZrQXHyUJyHnLJJwUxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ocUyg/btrXfMDrnFG/ukTqZrQXHyUJyHnLJJwUxk/img.png&quot; data-alt=&quot;도커는 컨테이너를 관리한다. (이미지 출처: [참고]1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ocUyg/btrXfMDrnFG/ukTqZrQXHyUJyHnLJJwUxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FocUyg%2FbtrXfMDrnFG%2FukTqZrQXHyUJyHnLJJwUxk%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;456&quot; height=&quot;344&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도커는 컨테이너를 관리한다. (이미지 출처: [참고]1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;'컨테이너 기반의 오픈소스 가상화 플랫폼'&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;서버에서 컨테이너란, 다양한 프로그램, 실행환경을 컨테이너로 추상화하고,동일한 인터페이스를 제공하여 프로그램의 배포 및 관리를 단순하게 해줍니다.&lt;br /&gt;백엔드 프로그램, 데이터베이스 서버, 메시지 큐등 어떠한 프로그램도 컨테이너로 추상화 할 수 있고,어떠한 환경에서도 실행할 수 있습니다.&lt;br /&gt;&lt;br /&gt;TMI) 구글은 모든 서비스를 컨테이너로 동작시키고, 매주 20억 개의 컨테이너를 구동한다고 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Container 컨테이너&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v3R60/btrXaChWHx2/pRuuK3a7zoXYI8aCaDS4LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v3R60/btrXaChWHx2/pRuuK3a7zoXYI8aCaDS4LK/img.png&quot; data-alt=&quot;도커의 컨테이너&amp;amp;nbsp;(이미지 출처: [참고]1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v3R60/btrXaChWHx2/pRuuK3a7zoXYI8aCaDS4LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv3R60%2FbtrXaChWHx2%2FpRuuK3a7zoXYI8aCaDS4LK%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;500&quot; height=&quot;389&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도커의 컨테이너&amp;nbsp;(이미지 출처: [참고]1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;'격리된 공간에서 프로세스가 동작하는 기술'&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;기존의 VMware, VirtualBox같은 가상머신은 Host OS위에 Guest OS 전체를 가상화하여 사용하는 방식을 이용하였습니다. 이 방식은 여러가지 OS를 가상화할 수 있고 사용법이 간단하지만, 무겁고 느려서 운영환경에서는 사용할 수 없었습니다.&lt;br /&gt;이러한 상황을 개선하기 위해 CPU의 가상화 기술을 이용한 KVM(Kernel-based Virtual Machine)과 반가상화 방식이 등장하였습니다. 이 방식은 Guest OS가 필요하긴 하지만 전체 OS를 가상화하는 방식이 아니였기 때문에 성능이 향상되었습니다.&lt;br /&gt;이러한 기술들은 OpenStack이나 AWS같은 클라우드 서비스에서 가상 컴퓨팅 기술의 기반이 되었습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUpQn/btrXgHu1Dn7/KGxnHytQLF8Ek4L2CcMQe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUpQn/btrXgHu1Dn7/KGxnHytQLF8Ek4L2CcMQe1/img.png&quot; data-alt=&quot;기존 방식과 Docker 방식&amp;amp;nbsp;(이미지 출처: [참고]1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUpQn/btrXgHu1Dn7/KGxnHytQLF8Ek4L2CcMQe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUpQn%2FbtrXgHu1Dn7%2FKGxnHytQLF8Ek4L2CcMQe1%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;516&quot; height=&quot;319&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 방식과 Docker 방식&amp;nbsp;(이미지 출처: [참고]1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;가상화 방식에 상관없이 추가적인 OS를 설치하여 가상화하는 방법은 성능 문제가 있습니다.&lt;br /&gt;이를 개선하기 위해 '프로세스 격리' 하는 방식이 등장합니다.&lt;br /&gt;&lt;br /&gt;이 방식을 리눅스에서는 리눅스 컨테이너라고 하고 단순히 프로세스를 격리시키기 때문에 가볍고 빠르게 동작합니다.&lt;br /&gt;CPU나 메모리는 딱 프로세스가 필요한 만큼만 추가로 사용하고 성능적으로도 손실이 거의 없습니다.&lt;br /&gt;도커의 컨테이너도 이와같이 작동하는 것입니다.&lt;br /&gt;&lt;br /&gt;하나의 서버에 여러개의 컨테이너를 실행하면 서로 영향을 미치지 않고 독립적으로 실행되어 마치 가벼운 VM을 사용하는 느낌을 줍니다. 또한 새로운 컨테이너를 만드는데 걸리는 시간은 1-2초로 가상머신과 비교도 안될 만큼 빠릅니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Image 이미지&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVf2zz/btrXbDUU02P/utriNValFlJFL8MyB43121/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVf2zz/btrXbDUU02P/utriNValFlJFL8MyB43121/img.png&quot; data-alt=&quot;도커의 이미지&amp;amp;nbsp;(이미지 출처: [참고]1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVf2zz/btrXbDUU02P/utriNValFlJFL8MyB43121/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVf2zz%2FbtrXbDUU02P%2FutriNValFlJFL8MyB43121%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;557&quot; height=&quot;318&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도커의 이미지&amp;nbsp;(이미지 출처: [참고]1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;'컨테이너 실행에 필요한 파일과 설정값을 포함하고 있는 것'&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;이미지는 상태값을 가지지 않고 변하지 않습니다. &lt;u&gt;컨테이너는 이미지를 실행한 상태&lt;/u&gt;라고 볼 수 있고, &lt;u&gt;같은 이미지에서 여러 개의 컨테이너를 생성&lt;/u&gt;할 수 있고, 컨테이너의 상태가 바뀌거나 컨테이너가 삭제되더라도 &lt;u&gt;이미지는 변하지 않고 남아있&lt;/u&gt;습니다.&lt;br /&gt;&lt;br /&gt;이미지는 컨테이너를 실행하기 위한 모든 정보를 가지고 있기 때문에 이미지를 통해 컨테이너를 생성할 수 있습니다.&lt;br /&gt;이미지는 Docker hub이나 Docker Registry 저장소에서 관리할 수 있고, 누구나 쉽게 만들고 배포할 수 있습니다.&lt;br /&gt;&lt;br /&gt;도커는 이미지를 만들기 위해 Dockerfile이라는 파일에 자체 DSL(Domain-specific language)언어를 이용하여 이미지 생성 과정을 적습니다.&lt;/blockquote&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;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;항상 공부해야지 생각만했던 도커를 알아보기 시작하였습니다.&lt;br /&gt;이번 시간에는 도커, 컨테이너, 이미지에 대한 전반적인 개념을 알아보았습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;오늘 공부한 내용을 까먹지 않기 위해 블로그에 적으면서 공부를 했습니다.&lt;br /&gt;확실히 읽기만 했을 때보다 글로 적으면서 하니까 머리속에 더 잘 기억됩니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다음에는 직접 도커를 설치해서 컨테이너를 만들어보고 이미지도 만들어서 배포까지 진행해보겠습니다.&lt;br /&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;1. &lt;a href=&quot;https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;초보를 위한 도커 안내서 - 도커란 무엇인가?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CI.CD/Docker</category>
      <category>docker</category>
      <category>lakelight</category>
      <category>도커</category>
      <category>도커란</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/139</guid>
      <comments>https://lakelight.tistory.com/139#entry139comment</comments>
      <pubDate>Thu, 26 Jan 2023 13:38:50 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Email 인증 시스템 구현</title>
      <link>https://lakelight.tistory.com/138</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;간단한 Email 인증 시스템을 만들어보겠습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동작 순서&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 이메일을 입력&lt;/li&gt;
&lt;li&gt;랜덤 코드를 생성&lt;/li&gt;
&lt;li&gt;Email 전송 라이브러리를 이용하여 코드를 사용자 Email에 전송&lt;/li&gt;
&lt;li&gt;사용자가 인증 코드를 입력하면 인증 성공 (시스템 내부에서 맞는지 체크)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dependencies 추가 [&lt;u&gt;참고로 스프링 3.0 기준입니다.&lt;/u&gt;]&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;dependencies {
    // 이메일 인증 관련 의존성
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '2.6.3'
    implementation group: 'com.sun.mail', name: 'jakarta.mail', version: '2.0.1'
    implementation group: 'com.sun.activation', name: 'jakarta.activation', version: '2.0.1'
    
    // html 렌더링을 위한 템플릿
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;디렉터리 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biS85J/btrUgisGdU7/0dlq1widacSMfe80Pt2HB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biS85J/btrUgisGdU7/0dlq1widacSMfe80Pt2HB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biS85J/btrUgisGdU7/0dlq1widacSMfe80Pt2HB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiS85J%2FbtrUgisGdU7%2F0dlq1widacSMfe80Pt2HB0%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;279&quot; height=&quot;265&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;공통으로 사용하는 global 패키지 내부에 email 패키지를 생성하여 구현하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이메일 인증에 사용할 이메일 설정&lt;/b&gt;&lt;/h4&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-origin-width=&quot;2763&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xZ5HK/btrUeYIJEzW/r9LW8zZjXR1VcWXzvbj1Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xZ5HK/btrUeYIJEzW/r9LW8zZjXR1VcWXzvbj1Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xZ5HK/btrUeYIJEzW/r9LW8zZjXR1VcWXzvbj1Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxZ5HK%2FbtrUeYIJEzW%2Fr9LW8zZjXR1VcWXzvbj1Rk%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;2763&quot; height=&quot;670&quot; data-origin-width=&quot;2763&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;구글 계정 설정&amp;nbsp;&amp;rarr; 보안 &amp;rarr; 2단계 인증을 사용으로 변경합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1289&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/soIZH/btrUhm9a1oM/ibCxwPUBdjE7LzwFRUQNT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/soIZH/btrUhm9a1oM/ibCxwPUBdjE7LzwFRUQNT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/soIZH/btrUhm9a1oM/ibCxwPUBdjE7LzwFRUQNT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsoIZH%2FbtrUhm9a1oM%2FibCxwPUBdjE7LzwFRUQNT0%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;1289&quot; height=&quot;327&quot; data-origin-width=&quot;1289&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;앱 선택은 &lt;b&gt;'메일'&lt;/b&gt;로 해주시고, 기기 선택은 &lt;b&gt;'Windows 컴퓨터'&lt;/b&gt;로 하고 생성을 클릭합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;1040&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wcr1A/btrUgQihHCD/1jKEhi3pCo3KyOqjFphqKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wcr1A/btrUgQihHCD/1jKEhi3pCo3KyOqjFphqKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wcr1A/btrUgQihHCD/1jKEhi3pCo3KyOqjFphqKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwcr1A%2FbtrUgQihHCD%2F1jKEhi3pCo3KyOqjFphqKk%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;665&quot; height=&quot;560&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;1040&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;다음과 같이 16자리 비밀번호가 생성되었다면 16자리 비밀번호를 메모장에 저장해둡니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2623&quot; data-origin-height=&quot;1565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8oNpn/btrUi3gZtfJ/H5XK0PJENleR2F9ypJOGK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8oNpn/btrUi3gZtfJ/H5XK0PJENleR2F9ypJOGK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8oNpn/btrUi3gZtfJ/H5XK0PJENleR2F9ypJOGK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8oNpn%2FbtrUi3gZtfJ%2FH5XK0PJENleR2F9ypJOGK0%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;2623&quot; height=&quot;1565&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2623&quot; data-origin-height=&quot;1565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;다음은 Gmail 앱 설정 탭에서 다음과 같이 설정해줍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.properties&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#Email Auth Setting
spring.mail.host=smtp.gmail.com #네이버로 할 시 변경 smtp.naver.com
spring.mail.port=587
spring.mail.username={사용자 Email}
spring.mail.password={발급받은 비밀번호 16자리}
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.auth=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EmailService.java&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import hooyn.base.global.exception.CustomException;
import hooyn.base.global.exception.ErrorCode;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.util.Random;

@Service
@RequiredArgsConstructor
public class EmailService {

    private final JavaMailSender emailSender;
    private final TemplateEngine templateEngine;

    //application.properties에서 사용자 Email 정보 가져오기
    @Value(&quot;${spring.mail.username}&quot;)
    private String from;

    //이메일 전송이 오래걸려서 비동기로 처리하기 위해 @Async 키워드를 사용하여 처리하였습니다.
    //다음과 같이 사용하기 위해서는 Application.class 에 @EnableAsync 을 추가해주어야 합니다.
    @Async
    public void sendAuthEmail(String email, String authCode) {
        try {
            MimeMessage emailForm = makeEmailForm(email, authCode);
            emailSender.send(emailForm);
        } catch (Exception e) {
            throw new CustomException(ErrorCode.BAD_REQUEST, e.getMessage());
        }
    }

    //이메일 인증을 위해 사용자에게 보낼 이메일 폼을 생성합니다.
    private MimeMessage makeEmailForm(String email, String authCode) throws MessagingException {
        MimeMessage message = emailSender.createMimeMessage();

        message.addRecipients(MimeMessage.RecipientType.TO, email); //보낼 사람 설정
        message.setSubject(&quot;LakeLight 회원가입 이메일 인증&quot;); //이메일 제목
        message.setFrom(from); //보내는 사람 설정
        message.setContent(setContext(authCode), &quot;text/html;charset=euc-kr&quot;); //setContext를 통해 html을 만들고 전송

        return message;
    }

    //HTML 제작하고 인증 코드도 HTML에서 보여줄 수 있도록 전달
    private String setContext(String authCode) {
        Context context = new Context();
        context.setVariable(&quot;code&quot;, authCode);
        return templateEngine.process(&quot;email&quot;, context);
    }

    //인증 코드 제작하는 코드
    //비동기 처리를 위해 public으로 하고 Controller 부분에서 파라미터로 전달
    public String makeAuthCode() {
        Random random = new Random();
        StringBuffer key = new StringBuffer();

        for (int i = 0; i &amp;lt; 8; i++) {
            int index = random.nextInt(3);

            switch (index) {
                case 0 -&amp;gt; key.append((char) (random.nextInt(26) + 97));
                case 1 -&amp;gt; key.append((char) (random.nextInt(26) + 65));
                case 2 -&amp;gt; key.append(random.nextInt(9));
            }
        }
        return key.toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EmailContorller.java&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import hooyn.base.global.response.ResponseWrapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping(&quot;/api/email/auth&quot;)
public class EmailController {

    private final HttpServletRequest request;
    private final EmailService emailService;

    @PostMapping(&quot;&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; confirmEmailAuth(@RequestBody ConfirmEmailAuthRequestDto dto) {

        //빠른 응답을 하고 로직은 비동기처리를 하기 위해 인증 코드를 생성하고,
        String authCode = emailService.makeAuthCode();
        //비동기로 처리하기 위해 인증 코드를 전달한다.
        emailService.sendAuthEmail(dto.getEmail(), authCode);

        return new ResponseEntity&amp;lt;&amp;gt;(new ResponseWrapper(request, HttpStatus.OK,
                true, &quot;이메일이 성공적으로 전송되었습니다.&quot;, &quot;AuthCode: [&quot; + authCode + &quot;]&quot; ), HttpStatus.OK);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ConfirmEmailAuthRequestDto.java&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import lombok.Getter;

@Getter
public class ConfirmEmailAuthRequestDto {

    private String email;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;index.html 위치와 코드&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;338&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JH40K/btrUh568zlW/Mggpmmj1zgNwdfZtiQY6iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JH40K/btrUh568zlW/Mggpmmj1zgNwdfZtiQY6iK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JH40K/btrUh568zlW/Mggpmmj1zgNwdfZtiQY6iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJH40K%2FbtrUh568zlW%2FMggpmmj1zgNwdfZtiQY6iK%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;338&quot; height=&quot;108&quot; data-origin-width=&quot;338&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div style=&quot;margin:100px;&quot;&amp;gt;
  &amp;lt;h1&amp;gt; 안녕하세요.&amp;lt;/h1&amp;gt;
  &amp;lt;h1&amp;gt; LakeLight Company 입니다.&amp;lt;/h1&amp;gt;
  &amp;lt;br&amp;gt;
  &amp;lt;p&amp;gt; 아래 코드를 회원가입 창으로 돌아가 입력해주세요.&amp;lt;/p&amp;gt;
  &amp;lt;br&amp;gt;

  &amp;lt;div style=&quot;font-family:verdana;&quot;&amp;gt;
    &amp;lt;h3 style=&quot;color:blue&quot;&amp;gt; 회원가입 인증 코드 입니다. &amp;lt;/h3&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;div th:text=&quot;${code}&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;br/&amp;gt;
&amp;lt;/div&amp;gt;


&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Postman으로 API 호출 후 결과 화면&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2155&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xCgRE/btrUkC4vL50/mS6D6tJlvO1PZhath0kwA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xCgRE/btrUkC4vL50/mS6D6tJlvO1PZhath0kwA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xCgRE/btrUkC4vL50/mS6D6tJlvO1PZhath0kwA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxCgRE%2FbtrUkC4vL50%2FmS6D6tJlvO1PZhath0kwA0%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;2155&quot; height=&quot;501&quot; data-origin-width=&quot;2155&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;비동기로 처리하기 때문에 355ms 만 걸리는 것을 확인할 수 있습니다. 비동기로 처리하기 전에는 로컬 PC 기준 3초가 걸렸는데, 비동기로 처리하고 355ms 로 줄어들었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터로 넘긴 email로 Email 온 화면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHlawy/btrUgfil2pO/ziHJlFkjn0n8py7mfP76a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHlawy/btrUgfil2pO/ziHJlFkjn0n8py7mfP76a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHlawy/btrUgfil2pO/ziHJlFkjn0n8py7mfP76a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHlawy%2FbtrUgfil2pO%2FziHJlFkjn0n8py7mfP76a0%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;1216&quot; height=&quot;553&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;553&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 늘 생각만으로 하던 이메일 인증에 대해서 구현해보았습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;라이브러리로 잘 구현되어 있어서&lt;br /&gt;생각했던 것보다는 어려운 점이 없었던 것 같습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다음 프로젝트를 할 때는 간단한게 적용해서 사용할 수 있을 것 같습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;또한 프로젝트에서 사용자에게 이메일을 보내야할 때 빠르게 구현할 수 있을 것 같습니다.&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;1.&lt;a href=&quot;https://velog.io/@rnqhstlr2297/Spring%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EC%9D%B8%EC%A6%9Dfeat.-%EB%84%A4%EC%9D%B4%EB%B2%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; Spring을 이용한 이메일 인증&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;a href=&quot;https://steady-coding.tistory.com/611&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Spring] @Async 사용 방법&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>email 인증</category>
      <category>spirng</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/138</guid>
      <comments>https://lakelight.tistory.com/138#entry138comment</comments>
      <pubDate>Thu, 22 Dec 2022 10:37:28 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] null이 아닌, 빈 컬렉션이나 배열을 반환하라</title>
      <link>https://lakelight.tistory.com/137</link>
      <description>&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;null을 반환하는 코드는 클라이언트가 &lt;br /&gt;null 상황을 처리하는 코드를 추가하도록 한다.&lt;br /&gt;&lt;br /&gt;컬렉션이나 배열 같은 컨테이너가 비었을 때 &lt;br /&gt;null을 반환하는 메서드를 사용할 때면 방어 코드를 추가해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;빈 컬렉션을 반환하는 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1671663884479&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public List&amp;lt;Object&amp;gt; getList() {
	return object.isEmpty() ? Collections.emptyList() : new ArrayList&amp;lt;&amp;gt;(object);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;길이가 0일 수도 있는 배열을 반환하는 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1671663963770&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

public Object[] getObjects() {
	return object.toArray(EMPTY_OBJECT_ARRAY);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 증가합니다.&lt;br /&gt;성능이 좋은 것도 아니기 때문에 null을 반환하지 않는 것이 좋은 방법 입니다.&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;a href=&quot;http://www.yes24.com/Product/Goods/65551284&quot;&gt;&lt;span&gt;이펙티브 자바 Effective Java 3/E - 조슈아 블로크&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;저/&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;개앞맵시&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;역&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Book Record/Effective Java 3E</category>
      <category>Effective Java</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/137</guid>
      <comments>https://lakelight.tistory.com/137#entry137comment</comments>
      <pubDate>Thu, 22 Dec 2022 08:08:00 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Lock - ReentrantLock</title>
      <link>https://lakelight.tistory.com/136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Lock&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티스레드, 동시성 프로그래밍에서 가장 중요한 개념은 Thread와 &lt;b&gt;Lock&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 자원에 대해 여러 Thread가 동시에 접근하는 것을 도와주는 도구입니다.&lt;/li&gt;
&lt;li&gt;Synchronized, 동기화하거나, 아니면 접근 자체를 직렬화한다고 표현합니다.&lt;/li&gt;
&lt;li&gt;멀티스레드 환경에서 여러 Thread가 Heap 메모리에 있는 객체나 자원을 접근할 때 동기화를 통해 접근을 통제해야 합니다.&lt;/li&gt;
&lt;li&gt;Lock을 통해서 공유 자원을 한 Thread만 접근할 수 있도록 통제할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Lock 기능&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 조건에 다라 지정한 수의 Thread만 자원에 접근하게 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;접근을 못한 Thread들은 줄을 세워, 대기하게 하고 들어갈 수 있을 때 다시 동작하게 할 수 있습니다.&lt;br /&gt;&amp;rarr; 대기 상태의 Thread는 Sleep 상태라고 하며, 다시 동작하게 하는 것은 notify() 를 통해서 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Lock 종류&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;암묵적 Lock : synchronized&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블럭이나 메서드 단위로 synchronized로 감싸서 Lock을 겁니다.&lt;/li&gt;
&lt;li&gt;어느 부분이 Lock 상태인지 명확하지 않아서 암묵적인 Lock이라고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Thread 간의 Lock을 획득하는 순서를 보장해주지 않습니다. (unFair)&lt;/li&gt;
&lt;li&gt;1개의 Thread만 공유 자원에 접근할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;명시적 Lock : ReentrantLock&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다중 스레드가 공유하는 주요 컬렉션 대신 완전히 독립적인 Lock을 채택하여 구현하는 Lock 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Thread 간의 Lock을 획득하는 순서를 보장할 수도 있고, 보장하지 않을 수도 있습니다. (Fair, unFair)&lt;/li&gt;
&lt;li&gt;Lock을 획득하려는 스레드의 개수가 4개 이상일 때 사용하면 좋습니다.&lt;/li&gt;
&lt;li&gt;여러개의 Thread가 공유 자원에 접근할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReentrantLock Code&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;private ResponseWrapper Function(Command command){

    this.lock.lock();

    //Logic();
    
    try {
        //Logic();
    } catch (Exception e) {
        //Logic();
    } finally {
        this.lock.unlock();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;위에 코드와 같이 필요한 부분에 Lock을 걸어주어 공유자원에 대해 스레드 접근을 통제할 수 있습니다.&lt;/blockquote&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;1. &lt;a href=&quot;https://jjaesang.github.io/java/2019/07/27/java-Lock-ReentrantLock.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Lock &amp;amp; ReentrantLock&lt;/a&gt;&lt;/p&gt;</description>
      <category>JAVA</category>
      <category>lock</category>
      <category>ReentrantLock</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/136</guid>
      <comments>https://lakelight.tistory.com/136#entry136comment</comments>
      <pubDate>Thu, 8 Dec 2022 17:52:31 +0900</pubDate>
    </item>
    <item>
      <title>[Architecture] MSA : MicroService Architecture</title>
      <link>https://lakelight.tistory.com/135</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;요즘 개발하면서 MSA를 정말 많이 보고 듣게 되어, &lt;br /&gt;개념에 대해 정리해보고자 글을 쓰게 되었습니다. &lt;br /&gt;&lt;br /&gt;제가 현재 하고 있는 프로젝트는 규모가 크지않아서 MSA를 적용하는 것에 대해서&lt;br /&gt;고민을 한 결과 적용을 안하는 것이 더 좋다고 판단하였습니다.&lt;br /&gt;&lt;br /&gt;하지만 다음 기회에 규모가 큰 프로젝트를 할 때 MSA를 적용해보면 좋을 것 같습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HD8lc/btrS6THT600/p11KMRItqHVr1AyGAE18Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HD8lc/btrS6THT600/p11KMRItqHVr1AyGAE18Rk/img.png&quot; data-alt=&quot;Monolithic Architecture VS MicroService Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HD8lc/btrS6THT600/p11KMRItqHVr1AyGAE18Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHD8lc%2FbtrS6THT600%2Fp11KMRItqHVr1AyGAE18Rk%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;643&quot; height=&quot;396&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Monolithic Architecture VS MicroService Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Monolithic&amp;nbsp;Architecture&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;소프트웨어의 모든 구성요소가 한 프로젝트에 통합되어 있는 형태&lt;/b&gt;입니다. 웹 개발을 예로 들면 웹 프로그램을 개발하기 위해 모듈별로 개발을 하고, 개발이 완료된 웹 어플리케이션을 하나의 결과물로 패키징하여 배포하는 형태를 말합니다. 웹의 경우에는 WAR 파일로 빌드외어 WAS에 배포하는 형태를 말합니다. 주로 소규모 프로젝트에서 사용합니다.&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;li&gt;배포 시간이 오래 걸립니다.&lt;/li&gt;
&lt;li&gt;한 Framework와 언어에 종속적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MSA : MicroService&amp;nbsp;Architecture&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 통해서 상호작용하며, 서비스의 접근점을 API 형태로 외부에 노출하고, 실질적인 세부 사항은 모두 추상화합니다. 내부의 구현 로직, 아키텍처와 프로그래밍 언어, 데이터베이스, 품질 유지 체계와 같은 기술적인 사항들은 서비스 API에 의해 철저하게 가려집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;하나의 비즈니스 범위에 맞춰 만들어지므로 하나의 기능만 수행&lt;/b&gt;합니다. 즉, 어플리케이션 출시처럼 하나의 목표를 향해 일하지만, &lt;b&gt;자기가 개발하는 서비스만 책임&lt;/b&gt;을 집니다. 추가로, &lt;b&gt;재활용이 가능하다는 장점&lt;/b&gt;이 있습니다.&lt;/li&gt;
&lt;li&gt;어플리케이션은 항상 기술 중립적 프로토콜을 사용해 통신하므로 서비스 구현 기술과는 무관합니다. 따라서 &lt;b&gt;마이크로서비스 기반의 어플리케이션을 다양한 언어와 기술로 구축&lt;/b&gt;할 수 있습니다.&lt;/li&gt;
&lt;li&gt;마이크로서비스는 SOA에서 사용되는 &lt;b&gt;집중화된 관리 체계를 사용하지 않습니다&lt;/b&gt;. 마이크로서비스 구현체의 공통적인 특징 중 하나는 ESB와 같은 무거운 제품에 의존하지 않는다는 점입니다. &lt;b&gt;REST 등 가벼운 통신 아키텍처 또는 kafka 등을 이용한 message stream&lt;/b&gt;을 주로 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MSA의 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스는 각각 모듈화 되어 있어, 이러한 &lt;b&gt;모듈끼리는 RPC 또는 message-driven API&lt;/b&gt; 등을 이용하여 통신합니다.&lt;/li&gt;
&lt;li&gt;서비스 단위로 적절한 수준에서 &lt;b&gt;기술 스택을 다르게 할 수 있습니다&lt;/b&gt;. 회사가 Java의 Spring 기반이라도 MSA를 적용하면 node.js를 이용한 모듈을 연동할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서비스 별로 독립적인 배포가 가능합니다&lt;/b&gt;. 따라서 CI&amp;amp;CD도 가볍게 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;각각의 &lt;b&gt;서비스의 부하에 따라 개별적으로 Scale-Out이 가능&lt;/b&gt;합니다. 메모리, CPU 등 상당 부분 이득입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MSA의 단점&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;구조가 복잡합니다.&lt;/b&gt; 서비스가 모두 분산되어 있기 때문에 개발자는 내부 시스템의 통신을 어떻게 가져가야 할지 정해야 합니다. 또한, 통신 장애와 서버 부하 등이 있을 경우 &lt;b&gt;어떻게 transaction을 유지&lt;/b&gt;할지 결정하고 구현해야 합니다.&lt;/li&gt;
&lt;li&gt;비즈니스에 대한 DB를 가지고 있는 서비스도 각기 다르고, 서비스 연결을 위해서는 통신이 포함되어야 하기 때문에 &lt;b&gt;트랜잭션 유지에 대한 고민&lt;/b&gt;이 필요합니다.&lt;br /&gt;&amp;rarr; 보상 트랜잭션 또는 부분적으로 composite 서비스로 병합을 고려해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;통합 테스트가 어렵습니다&lt;/b&gt;. &lt;b&gt;개발 환경과 실제 운영환경을 동일하게 가져가는 것은 쉽지 않&lt;/b&gt;습니다.&lt;br /&gt;&lt;span&gt;&amp;rarr;&lt;span&gt; 서비스 레지스트리, 모니터링, 개발/ 배포 자동화 기술을 고려해야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;서비스 1개를 재배포 한다고 할 때, &lt;b&gt;다른 서비스들과 연계가 정상적으로 이루어지는지 테스트&lt;/b&gt; 해야 합니다.&amp;nbsp;&lt;br /&gt;&lt;span&gt;&amp;rarr;&lt;span&gt; 관련 개발 조직 간 roll-out 계획을 마련하고, dependency의 관리 체계를 수립해야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Microservice Architecture 구성도&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;937&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tkLy7/btrS6fqQ7T5/YZ8stvoagtZsqbOJRKT0fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tkLy7/btrS6fqQ7T5/YZ8stvoagtZsqbOJRKT0fk/img.png&quot; data-alt=&quot;이미지 출처:&amp;amp;amp;nbsp;https://wonit.tistory.com/490&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tkLy7/btrS6fqQ7T5/YZ8stvoagtZsqbOJRKT0fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtkLy7%2FbtrS6fqQ7T5%2FYZ8stvoagtZsqbOJRKT0fk%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;714&quot; height=&quot;556&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;937&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처:&amp;amp;nbsp;https://wonit.tistory.com/490&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Service Mesh 구성도&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;943&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8zxkj/btrS6hPvBYD/h1kGzKhZR5tasqoh0vmexK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8zxkj/btrS6hPvBYD/h1kGzKhZR5tasqoh0vmexK/img.png&quot; data-alt=&quot;이미지 출처:&amp;amp;amp;nbsp;https://wonit.tistory.com/490&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8zxkj/btrS6hPvBYD/h1kGzKhZR5tasqoh0vmexK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8zxkj%2FbtrS6hPvBYD%2Fh1kGzKhZR5tasqoh0vmexK%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;703&quot; height=&quot;547&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처:&amp;amp;nbsp;https://wonit.tistory.com/490&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;만약 다른 언어와 다른 프레임워크로 작동한다면 &lt;b&gt;Sidecar Pattern&lt;/b&gt;을 이용하여, 다른 플랫폼 언어로 작성된 서버에 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;아직 모르는 부분이 많다는 것을 느꼈습니다. &lt;br /&gt;MSA를 구축하고, Spring Cloud Gateway를 통해 연결하고,&lt;br /&gt;kafka를 통해 sync를 맞춰주는 작업들&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;하나하나 배워나가겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;오늘도 포스팅 읽어주셔서 감사합니다.&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;1. &lt;a href=&quot;https://wooaoe.tistory.com/57&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[MSA] MSA란 무엇인가? 개념 이해하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;a href=&quot;https://wonit.tistory.com/490&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[마이크로서비스] MSA의 핵심 구성 요소 - Service Mesh&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;a href=&quot;https://velog.io/@ragnarok_code/%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-vs-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;모놀리식 아키텍처 vs 마이크로서비스&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Architecture</category>
      <category>Architecture</category>
      <category>MicrosoftService Architecture</category>
      <category>MSA</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/135</guid>
      <comments>https://lakelight.tistory.com/135#entry135comment</comments>
      <pubDate>Thu, 8 Dec 2022 14:30:15 +0900</pubDate>
    </item>
    <item>
      <title>[Connect Plc to Server] N + 1 Problem 해결 사례</title>
      <link>https://lakelight.tistory.com/134</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;날짜 별 로그 작업을 하던 중 로그를 확인 해보니, 한번의 API에서 회원 수 만큼&lt;br /&gt;&lt;/span&gt;Query가 발생하고 있었습니다. 흔히 알고 있는 N+1 문제로 인식하고 해결을 하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Query Log&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1670389174925&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INFO  22-12-07 13:48:39[http-nio-8080-exec-3] [sqltiming:373] - select account0_.id as id1_0_, account0_.account_roles as account_5_0_, account0_.user_id as 
user_id2_0_, account0_.user_name as user_nam3_0_, account0_.user_pw as user_pw4_0_ from account 
account0_ 
 {executed in 1 msec}
INFO  22-12-07 13:48:39[http-nio-8080-exec-3] [sqltiming:373] - select accountrol0_.id as id1_1_0_, accountrol0_.authority as authorit2_1_0_, accountrol0_.normal 
as normal3_1_0_, accountrol0_.vip as vip4_1_0_ from account_roles accountrol0_ where accountrol0_.id=1 
 {executed in 1 msec}
INFO  22-12-07 13:48:39[http-nio-8080-exec-3] [sqltiming:373] - select accountrol0_.id as id1_1_0_, accountrol0_.authority as authorit2_1_0_, accountrol0_.normal 
as normal3_1_0_, accountrol0_.vip as vip4_1_0_ from account_roles accountrol0_ where accountrol0_.id=2 
 {executed in 1 msec}
INFO  22-12-07 13:48:39[http-nio-8080-exec-3] [sqltiming:373] - select accountrol0_.id as id1_1_0_, accountrol0_.authority as authorit2_1_0_, accountrol0_.normal 
as normal3_1_0_, accountrol0_.vip as vip4_1_0_ from account_roles accountrol0_ where accountrol0_.id=3 
 {executed in 0 msec}
INFO  22-12-07 13:48:39[http-nio-8080-exec-3] [sqltiming:373] - select accountrol0_.id as id1_1_0_, accountrol0_.authority as authorit2_1_0_, accountrol0_.normal 
as normal3_1_0_, accountrol0_.vip as vip4_1_0_ from account_roles accountrol0_ where accountrol0_.id=4 
 {executed in 1 msec}
INFO  22-12-07 13:48:39[http-nio-8080-exec-3] [LogUtil:70] - 
[REQUEST] GET - /api/account/list 200 - 0.016
ClientIP : 0:0:0:0:0:0:0:1
RequestBody :  [] 
Response : {
  &quot;timestamp&quot; : &quot;2022-12-07 13:48&quot;,
  &quot;path&quot; : &quot;/api/account/list [GET]&quot;,
  &quot;status&quot; : &quot;OK&quot;,
  &quot;code&quot; : 200,
  &quot;isSuccess&quot; : true,
  &quot;message&quot; : &quot;전체 회원 목록이 조회되었습니다.&quot;,
  &quot;responseData&quot; : [ {} ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;API는 전체 회원을 조회하는 기능으로, Response에서 Entity를 그대로 반환하고 있었습니다. 항상 이러한 문제가 발생할 수 있기 때문에 DTO를 통해 반환하는 것을 알고 있었으나, 무슨 이유에서인지 Entity를 그대로 반환하고 있었습니다. 그래서 빠르게 수정을 하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오류 해결&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Query가 발생하고 있던 accountRoles 부분에 LAZY 전략을 적용하였습니다. &lt;/b&gt;&lt;br /&gt;LAZY: 지연 로딩으로 Proxy객체로 가지고 있다가, accountRoles에 접근을 했을 때 Query를 실행하여 데이터를 가져옵니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@OneToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = &quot;accountRoles&quot;)
private AccountRoles accountRoles;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리고 다음과 같은 DTO를 생성하여 Return 해주는 코드로 변경하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ListAccountResponseDto.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public static class ListAccountResponseDto {
    private Long id;
    private String userId;
    private String userName;

    @Builder
    public ListAccountResponseDto(Long id, String userId, String userName) {
        this.id = id;
        this.userId = userId;
        this.userName = userName;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AccountService.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Slf4j
@Service
@RequiredArgsConstructor
public class AccountService {
    public List&amp;lt;ListAccountResponseDto&amp;gt; listAccount() {
        List&amp;lt;ListAccountResponseDto&amp;gt; response = new ArrayList&amp;lt;&amp;gt;();

        List&amp;lt;Account&amp;gt; accounts = accountRepository.findAll();
        for (Account account : accounts)
            response.add(buildListAccountResponseDto(account));

        return response;
    }

    private static ListAccountResponseDto buildListAccountResponseDto(Account account) {
        return ListAccountResponseDto.builder().id(account.getId()).userId(account.getUserId()).userName(account.getUserName()).build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과 화면&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;INFO  22-12-07 13:56:33[http-nio-8080-exec-2] [sqltiming:373] - select account0_.id as id1_0_, account0_.account_roles as account_5_0_, account0_.user_id as 
user_id2_0_, account0_.user_name as user_nam3_0_, account0_.user_pw as user_pw4_0_ from account 
account0_ 
 {executed in 1 msec}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Query가 다음과 같이 수정되었습니다. 현재는 회원 수가 많지 않아서 괜찮지만 몇백만명이 됬었다면 성능 이슈가 발생했을 것입니다.&lt;/blockquote&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같은 실수는 다시는 없어야 할 것입니다. &lt;br /&gt;지금은 배포전이라서 사용자가 없지만,&lt;br /&gt;사용자가 있었다면 이는 바로 성능 문제로 이어졌을 것입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다음에는 이런 실수를 하지 않도록 코딩을 할 것입니다.&lt;/p&gt;</description>
      <category>Project/Connect Plc to Server</category>
      <category>n+1</category>
      <category>query</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/134</guid>
      <comments>https://lakelight.tistory.com/134#entry134comment</comments>
      <pubDate>Wed, 7 Dec 2022 14:11:29 +0900</pubDate>
    </item>
    <item>
      <title>[Miscellaneous] ngrok 이용한 Localj 개발 환경 접근</title>
      <link>https://lakelight.tistory.com/133</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsyhWk/btrSZY97qxe/SlmtdtjEuegsKN5XdoU500/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsyhWk/btrSZY97qxe/SlmtdtjEuegsKN5XdoU500/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsyhWk/btrSZY97qxe/SlmtdtjEuegsKN5XdoU500/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsyhWk%2FbtrSZY97qxe%2FSlmtdtjEuegsKN5XdoU500%2Fimg.jpg&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;190&quot; height=&quot;107&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;카페 또는 집에서 업무 중 개발 중인 Spring 서버에 접근하도록 해줘야 할 때,&lt;br /&gt;로컬에서 개발중인 웹서버, WAS서버를 외부에 있는 사람에게 접근하게 해줘야 할 때,&lt;br /&gt;&lt;br /&gt;Ngrok를 이용해 외부에서 로컬 개발 환경 localhost로 접근할 수 있도록 할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Download ngrok&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1670369015741&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;ngrok - download&quot; data-og-description=&quot;Install ngrok via Homebrew $ brew install ngrok/ngrok/ngrok Download ZIP file Intel (AMD64) Apple Silicon (ARM64) Then unzip ngrok from the terminal $ Install ngrok via Chocolatey $ choco install ngrok Download ZIP file Windows (64-bit) Windows (32-bit) Do&quot; data-og-host=&quot;ngrok.com&quot; data-og-source-url=&quot;https://ngrok.com/download&quot; data-og-url=&quot;https://ngrok.com/download&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ngrok.com/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ngrok.com/download&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ngrok - download&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Install ngrok via Homebrew $ brew install ngrok/ngrok/ngrok Download ZIP file Intel (AMD64) Apple Silicon (ARM64) Then unzip ngrok from the terminal $ Install ngrok via Chocolatey $ choco install ngrok Download ZIP file Windows (64-bit) Windows (32-bit) Do&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ngrok.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ngrok&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ngrok는 NAT 및 방화벽 뒤에 있는 로컬 서버를 보안 터널을 통해 공용 인터넷에 접속하는 방법을 제공합니다. &lt;span&gt;ngrok 사이트에는 Secure tunnels to localhost라고 명시되어 있고 &lt;/span&gt;간단하게 방화벽을 넘어, 외부에서 로컬에 접근할 수 있도록 하는 터널을 구성해주는 프로그램입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 통신을 할 수 있는 API를 개발했을 때 API에 접근할 수 있는 서버가 필요합니다. 개발에 사용하는 PC는 외부에서 접속 가능한 상용 서버가 아니기 때문에 도메인을 구매하거나, AWS를 이용한 서버 호스팅을 이용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ngrok를 이용하면 외부에서 로컬 PC로 접근을 가능하도록 하여, 외부에서 PC에 접근을 할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ngrok 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvQQYQ/btrSYnQjpig/nfsSTiKkEUmAkVRgNbxM61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvQQYQ/btrSYnQjpig/nfsSTiKkEUmAkVRgNbxM61/img.png&quot; data-alt=&quot;다운로드 한 zip 압축해제 후 ngrok.exe 실행한 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvQQYQ/btrSYnQjpig/nfsSTiKkEUmAkVRgNbxM61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvQQYQ%2FbtrSYnQjpig%2FnfsSTiKkEUmAkVRgNbxM61%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;1958&quot; height=&quot;1020&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다운로드 한 zip 압축해제 후 ngrok.exe 실행한 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1670369492391&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ngrok http 8080 #nginx 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s533G/btrS0FWuGvG/sxWCXQK3LUWKcStkBjK0DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s533G/btrS0FWuGvG/sxWCXQK3LUWKcStkBjK0DK/img.png&quot; data-alt=&quot;ngrok 실행된 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s533G/btrS0FWuGvG/sxWCXQK3LUWKcStkBjK0DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs533G%2FbtrS0FWuGvG%2FsxWCXQK3LUWKcStkBjK0DK%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;1924&quot; height=&quot;1020&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ngrok 실행된 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;https://0da2-14-47-96-237.jp.ngrok.io를 통해 외부에서 로컬 환경으로 접근할 수 있습니다. 만약 집에서 개발을 하고 있는데, 서버를 열어야 할 때 이 방법을 사용하면 편리할 것 같습니다. 포트 포워딩은 카페 같은 곳에서는 할 수 없기 때문에 이 방법을 사용하면 어디서든 서버를 배포하는 효과를 볼 수 있을 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Session Expires&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ngrok는 무료 사용자에게 2시간의 세션 시간을 제공합니다. 2시간 후에는 Forwarding URL이 변경되기 때문에 불편한 점이 있을 수 있습니다. 이는 간단한 방법으로 해제할 수 있습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1670370292248&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;ngrok - Online in One Line&quot; data-og-description=&quot;Zero Trust Add SSO, Mutual TLS, IP Policy, and webhook signature verification.&quot; data-og-host=&quot;ngrok.com&quot; data-og-source-url=&quot;https://ngrok.com/&quot; data-og-url=&quot;https://ngrok.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ngrok.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ngrok.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ngrok - Online in One Line&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Zero Trust Add SSO, Mutual TLS, IP Policy, and webhook signature verification.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ngrok.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해당 ngrok 메인 홈페이지에 들어가서 로그인을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAf1Ez/btrS0K4xHw0/auTiouPzjU7nKPdJ6KWV71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAf1Ez/btrS0K4xHw0/auTiouPzjU7nKPdJ6KWV71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAf1Ez/btrS0K4xHw0/auTiouPzjU7nKPdJ6KWV71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAf1Ez%2FbtrS0K4xHw0%2FauTiouPzjU7nKPdJ6KWV71%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;3000&quot; height=&quot;863&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;왼쪽 메뉴에 Your Authtoken을 클릭하여 다음과 같은 화면이 나오도록 합니다. 그리고 Command Line에 있는 명령어를 ngrok.exe를 실행한 후 입력하면 설정파일에 authtoken이 등록되어 무제한으로 이용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MYffe/btrS0D5rwta/4rgmXyJzi1bs993Y2vgazK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MYffe/btrS0D5rwta/4rgmXyJzi1bs993Y2vgazK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MYffe/btrS0D5rwta/4rgmXyJzi1bs993Y2vgazK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMYffe%2FbtrS0D5rwta%2F4rgmXyJzi1bs993Y2vgazK%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;1958&quot; height=&quot;1020&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Authtoken이 저장된 위치가 나오며 등록이 된 것을 알 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NRc6E/btrS1aaPQ8T/Cp3xj9xbv90uOWFjAoRDC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NRc6E/btrS1aaPQ8T/Cp3xj9xbv90uOWFjAoRDC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NRc6E/btrS1aaPQ8T/Cp3xj9xbv90uOWFjAoRDC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNRc6E%2FbtrS1aaPQ8T%2FCp3xj9xbv90uOWFjAoRDC1%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;1924&quot; height=&quot;1020&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;다시 ngrok http 8080을 입력하면 Session Expires가 사라진 것을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;ngrok는 방화벽을 우회하여 로컬 PC로 접근을 해주는 만큼 보안에 취약할 것입니다. &lt;br /&gt;때문에 기업에서 사용하기에는 무리가 있어 보이고, 개인적으로 테스트할 때 사용하면, &lt;br /&gt;포트포워딩을 하는 방법 보다 더 편리하고 유연하게 사용할 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Miscellaneous</category>
      <category>Localhost</category>
      <category>ngrok</category>
      <category>port forwarding</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/133</guid>
      <comments>https://lakelight.tistory.com/133#entry133comment</comments>
      <pubDate>Wed, 7 Dec 2022 08:55:59 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] Git Push 시 Jenkins 자동빌드</title>
      <link>https://lakelight.tistory.com/131</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1670309224752&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Jenkins] Jenkins - Git 연동&quot; data-og-description=&quot;이번에는 Github와 Jenkins를 연동해보겠습니다. Github를 설치한 상황이라고 가정하고 시작하겠습니다. Dashboard &amp;gt; Jenkins 관리 &amp;gt; Global Tool Configuration 에 들어갑니다. Git 정보를 입력합니다. &amp;gt; 입력 후 맨 &quot; data-og-host=&quot;lakelight.tistory.com&quot; data-og-source-url=&quot;https://lakelight.tistory.com/130&quot; data-og-url=&quot;https://lakelight.tistory.com/130&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cDWJg8/hyQNXkozS9/2oYxp4c3n4a6rZWge5Z1Kk/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102,https://scrap.kakaocdn.net/dn/bLdUgA/hyQNMDaaDc/0U1GtyowfHgLrpQjjtFeaK/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102,https://scrap.kakaocdn.net/dn/ofTGS/hyQNQFym1y/aXhMARRS79qr6U8lHgHlpK/img.png?width=3006&amp;amp;height=1598&amp;amp;face=0_0_3006_1598&quot;&gt;&lt;a href=&quot;https://lakelight.tistory.com/130&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lakelight.tistory.com/130&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cDWJg8/hyQNXkozS9/2oYxp4c3n4a6rZWge5Z1Kk/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102,https://scrap.kakaocdn.net/dn/bLdUgA/hyQNMDaaDc/0U1GtyowfHgLrpQjjtFeaK/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102,https://scrap.kakaocdn.net/dn/ofTGS/hyQNQFym1y/aXhMARRS79qr6U8lHgHlpK/img.png?width=3006&amp;amp;height=1598&amp;amp;face=0_0_3006_1598');&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;[Jenkins] Jenkins - Git 연동&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 Github와 Jenkins를 연동해보겠습니다. Github를 설치한 상황이라고 가정하고 시작하겠습니다. Dashboard &amp;gt; Jenkins 관리 &amp;gt; Global Tool Configuration 에 들어갑니다. Git 정보를 입력합니다. &amp;gt; 입력 후 맨&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lakelight.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이전 포스팅에 이어서 Git에서 Push 했을 때 자동으로 빌드파일을&lt;br /&gt;만들도록 스크립트를 수정하였습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Jenkins 자동 빌드 솔루션&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원래 기존 Pipeline Script 코드입니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670309304978&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pipeline{
    agent any
    stages{
        stage('Checkout'){
            steps{
                git branch: 'master',
                credentialsId: 'github_access_token',
                url: 'https://github.com/hooyn/GraftingProj.git'
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자동으로 빌드하도록 아래와 같은 코드를 추가하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670309346836&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pipeline{
    agent any
    stages{
        stage('Checkout'){
            steps{
                git branch: 'master',
                credentialsId: 'github_access_token',
                url: 'https://github.com/hooyn/GraftingProj.git'
            }
        }
        
        stage('Build') {
            steps {
                bat &quot;./gradlew clean build&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오류 해결&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지속적으로 아래와 같은 오류가 발생하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;java.io.IOException: CreateProcess error=2, 지정된 파일을 찾을 수 없습니다
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2678&quot; data-origin-height=&quot;1581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DSRLX/btrSWxSA0oH/KKucdS9nyrQjk3IpHLQ4h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DSRLX/btrSWxSA0oH/KKucdS9nyrQjk3IpHLQ4h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DSRLX/btrSWxSA0oH/KKucdS9nyrQjk3IpHLQ4h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDSRLX%2FbtrSWxSA0oH%2FKKucdS9nyrQjk3IpHLQ4h1%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;2678&quot; height=&quot;1581&quot; data-origin-width=&quot;2678&quot; data-origin-height=&quot;1581&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;pre id=&quot;code_1670309454994&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pipeline{
    agent any
    stages{
        stage('Checkout'){
            steps{
                git branch: 'master',
                credentialsId: 'github_access_token',
                url: 'https://github.com/hooyn/GraftingProj.git'
            }
        }
        
        stage('Build') {
            steps {
                sh &quot;./gradlew clean build&quot; #-&amp;gt; bat &quot;./gradlew clean build&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Build 부분에 sh를 통해서 명령을 입력했으나, sh를 bat으로 변경하였더니 오류를 해결할 수 있었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;결과: Windows에서 sh를 사용할 수 없다고 합니다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sh&lt;/b&gt;는 Windows에서 지원하지 않아 &lt;b&gt;bat&lt;/b&gt;을 사용하거나 설치해야 합니다.&amp;nbsp; &lt;a href=&quot;https://stackoverflow.com/questions/45140614/jenkins-pipeline-sh-fail-with-cannot-run-program-nohup-on-windows&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[자세한 내용]&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;빌드 파일 생성 위치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dashboard &amp;gt; Jenkins 관리 &amp;gt; Configure System(시스템 설정)에서 &lt;b&gt;홈 디렉터리&lt;/b&gt;를 보시면 확인할 수 있습니다. &lt;b&gt;&lt;a href=&quot;https://joyfulhome.tistory.com/187&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(변경가능)&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3783&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qcVul/btrSUR5d2Cp/AZCXjU5XaFul9ccS1JZUg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qcVul/btrSUR5d2Cp/AZCXjU5XaFul9ccS1JZUg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qcVul/btrSUR5d2Cp/AZCXjU5XaFul9ccS1JZUg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqcVul%2FbtrSUR5d2Cp%2FAZCXjU5XaFul9ccS1JZUg0%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;3783&quot; height=&quot;625&quot; data-origin-width=&quot;3783&quot; data-origin-height=&quot;625&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;blockquote data-ke-style=&quot;style3&quot;&gt;{홈 디렉터리}/workspace/jenkins-project-name/build/libs 에 보시면 .jar 파일이 생성된 것을 확인할 수 있습니다.&lt;br /&gt;&lt;br /&gt;**Windows 탐색기를 통해서 확인하실 때는 보기 옵션에서 숨긴 항목 표시를 체크 해주시면 됩니다.**&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHk1eO/btrSUSpuzFo/KuWAlMU3skZj7KGvfp0zU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHk1eO/btrSUSpuzFo/KuWAlMU3skZj7KGvfp0zU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHk1eO/btrSUSpuzFo/KuWAlMU3skZj7KGvfp0zU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHk1eO%2FbtrSUSpuzFo%2FKuWAlMU3skZj7KGvfp0zU1%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;1833&quot; height=&quot;320&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;320&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이제는 자동으로 빌드 파일도 생성했으니, 자동 배포까지 해보겠습니다.&lt;br /&gt;&lt;/span&gt;감사합니다.&lt;/blockquote&gt;</description>
      <category>CI.CD/Jenkins</category>
      <category>gradle</category>
      <category>jar</category>
      <category>jenkins</category>
      <category>자동 빌드</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/131</guid>
      <comments>https://lakelight.tistory.com/131#entry131comment</comments>
      <pubDate>Tue, 6 Dec 2022 16:00:55 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] Jenkins - Git 연동</title>
      <link>https://lakelight.tistory.com/130</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1670310085373&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Jenkins] Jenkins 설치 - Windows&quot; data-og-description=&quot;젠킨스 젠킨스는 소프트웨어 개발 시 지속적 통합 서비스를 제공하는 툴입니다. 다수의 개발자들이 하나의 프로그램을 개발할 때 버전 충돌을 방지하기 위해 각자 작업한 내용을 공유 영역에 &quot; data-og-host=&quot;lakelight.tistory.com&quot; data-og-source-url=&quot;https://lakelight.tistory.com/128&quot; data-og-url=&quot;https://lakelight.tistory.com/128&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cnHHXF/hyQNREuwp1/GzKbZreODtrjniPkzHIhAK/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102,https://scrap.kakaocdn.net/dn/Lrzl9/hyQNXY1qPr/szpWZgB3y38JutCTfaqSY0/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102&quot;&gt;&lt;a href=&quot;https://lakelight.tistory.com/128&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lakelight.tistory.com/128&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cnHHXF/hyQNREuwp1/GzKbZreODtrjniPkzHIhAK/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102,https://scrap.kakaocdn.net/dn/Lrzl9/hyQNXY1qPr/szpWZgB3y38JutCTfaqSY0/img.jpg?width=530&amp;amp;height=349&amp;amp;face=252_34_315_102');&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;[Jenkins] Jenkins 설치 - Windows&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;젠킨스 젠킨스는 소프트웨어 개발 시 지속적 통합 서비스를 제공하는 툴입니다. 다수의 개발자들이 하나의 프로그램을 개발할 때 버전 충돌을 방지하기 위해 각자 작업한 내용을 공유 영역에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lakelight.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이번에는 Github와 Jenkins를 연동해보겠습니다.&lt;br /&gt;Github를 설치한 상황이라고 가정하고 시작하겠습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dashboard &amp;gt; Jenkins 관리 &amp;gt; Global Tool Configuration 에 들어갑니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;1853&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zEHlE/btrSUSINeZI/oopWEGTjMEbN35qNhb84s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zEHlE/btrSUSINeZI/oopWEGTjMEbN35qNhb84s0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zEHlE/btrSUSINeZI/oopWEGTjMEbN35qNhb84s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzEHlE%2FbtrSUSINeZI%2FoopWEGTjMEbN35qNhb84s0%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;3840&quot; height=&quot;1853&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;1853&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;b&gt;Git 정보를 입력합니다. &amp;gt; 입력 후 맨 아래 Save를 눌러서 저장합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3231&quot; data-origin-height=&quot;994&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OBHmX/btrSUp1igZK/tfGHQhKryFTyhdzJqgWsMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OBHmX/btrSUp1igZK/tfGHQhKryFTyhdzJqgWsMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OBHmX/btrSUp1igZK/tfGHQhKryFTyhdzJqgWsMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOBHmX%2FbtrSUp1igZK%2FtfGHQhKryFTyhdzJqgWsMK%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;3231&quot; height=&quot;994&quot; data-origin-width=&quot;3231&quot; data-origin-height=&quot;994&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;b&gt;다음은 Github에 로그인을 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Setting &amp;gt; Developer settings(메뉴 맨 아래) &amp;gt; Personal access tokens &amp;gt; Tokens(classic) &amp;gt; Generate new token &amp;gt; New Personal access token (classic)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2446&quot; data-origin-height=&quot;1782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjSYub/btrSVoObJtV/rsLKA5pxjHh50hk7Bhublk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjSYub/btrSVoObJtV/rsLKA5pxjHh50hk7Bhublk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjSYub/btrSVoObJtV/rsLKA5pxjHh50hk7Bhublk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjSYub%2FbtrSVoObJtV%2FrsLKA5pxjHh50hk7Bhublk%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;2446&quot; height=&quot;1782&quot; data-origin-width=&quot;2446&quot; data-origin-height=&quot;1782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Select scopes는 모두 체크해주었고, 기한은 적당하게 설정하여 토큰을 생성하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Github Webhook 등록&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Settings &amp;gt; Webhooks &amp;gt; Add webhook&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3814&quot; data-origin-height=&quot;1564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nZRbt/btrSURQCC6g/ygBUTMOj31UrJOK3sQE2E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nZRbt/btrSURQCC6g/ygBUTMOj31UrJOK3sQE2E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nZRbt/btrSURQCC6g/ygBUTMOj31UrJOK3sQE2E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnZRbt%2FbtrSURQCC6g%2FygBUTMOj31UrJOK3sQE2E1%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;3814&quot; height=&quot;1564&quot; data-origin-width=&quot;3814&quot; data-origin-height=&quot;1564&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2404&quot; data-origin-height=&quot;1705&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NYgpn/btrSUIl31e7/aHVYambhgeKnTi9yekDqR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NYgpn/btrSUIl31e7/aHVYambhgeKnTi9yekDqR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NYgpn/btrSUIl31e7/aHVYambhgeKnTi9yekDqR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNYgpn%2FbtrSUIl31e7%2FaHVYambhgeKnTi9yekDqR1%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;2404&quot; height=&quot;1705&quot; data-origin-width=&quot;2404&quot; data-origin-height=&quot;1705&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Payload URL은 아래 링크를 통해 ngrok URL을 받아서 뒤에 /github-webhook/을 붙입니다. 외부 IP에서 접속할 수 있어야 하기 때문에 포트포워딩 해주는 ngrok를 이용하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/snt2525/221287819314&quot;&gt;테스트를 위한 ngrok URL 받기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이제 생성된 토큰을 Jenkins에 등록해보겠습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dashboard &amp;gt; Jenkins 관리 &amp;gt; Manage Credentials &amp;gt; (global) &amp;gt; Add Credentials &amp;gt; Create&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2729&quot; data-origin-height=&quot;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5DIyM/btrSRQSFd9e/DMqyLpGPWEEFUGO0IK0Xj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5DIyM/btrSRQSFd9e/DMqyLpGPWEEFUGO0IK0Xj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5DIyM/btrSRQSFd9e/DMqyLpGPWEEFUGO0IK0Xj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5DIyM%2FbtrSRQSFd9e%2FDMqyLpGPWEEFUGO0IK0Xj0%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;2729&quot; height=&quot;904&quot; data-origin-width=&quot;2729&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3325&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KWKTn/btrSXpFMVYC/XQ8insiIKm7QgqWs5nKpkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KWKTn/btrSXpFMVYC/XQ8insiIKm7QgqWs5nKpkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KWKTn/btrSXpFMVYC/XQ8insiIKm7QgqWs5nKpkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKWKTn%2FbtrSXpFMVYC%2FXQ8insiIKm7QgqWs5nKpkK%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;3325&quot; height=&quot;722&quot; data-origin-width=&quot;3325&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9uxYX/btrSVpsQMqH/esSYGW0IIledyWagMm7q80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9uxYX/btrSVpsQMqH/esSYGW0IIledyWagMm7q80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9uxYX/btrSVpsQMqH/esSYGW0IIledyWagMm7q80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9uxYX%2FbtrSVpsQMqH%2FesSYGW0IIledyWagMm7q80%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;3000&quot; height=&quot;1513&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1513&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;b&gt;Jenkins Pipeline 프로젝트 생성합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;New Item &amp;gt; Create a job&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qXmmy/btrSUqeV9p1/WVxFwqutPHJvnPSy451Xy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qXmmy/btrSUqeV9p1/WVxFwqutPHJvnPSy451Xy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qXmmy/btrSUqeV9p1/WVxFwqutPHJvnPSy451Xy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqXmmy%2FbtrSUqeV9p1%2FWVxFwqutPHJvnPSy451Xy1%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;3000&quot; height=&quot;1454&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1454&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;b&gt;파이프라인을 생성합니다. Pipeline을 선택하고 OK&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;1598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGZxTL/btrSUAVTgtT/IzVVFvuTdi4l1lOmibPpVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGZxTL/btrSUAVTgtT/IzVVFvuTdi4l1lOmibPpVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGZxTL/btrSUAVTgtT/IzVVFvuTdi4l1lOmibPpVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGZxTL%2FbtrSUAVTgtT%2FIzVVFvuTdi4l1lOmibPpVk%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;3006&quot; height=&quot;1598&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;1598&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2636&quot; data-origin-height=&quot;1466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ULpIj/btrSRQd6YvC/YeVtW6OymX9N9SxaKuA4Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ULpIj/btrSRQd6YvC/YeVtW6OymX9N9SxaKuA4Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ULpIj/btrSRQd6YvC/YeVtW6OymX9N9SxaKuA4Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FULpIj%2FbtrSRQd6YvC%2FYeVtW6OymX9N9SxaKuA4Ik%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;2636&quot; height=&quot;1466&quot; data-origin-width=&quot;2636&quot; data-origin-height=&quot;1466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2699&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQpoyL/btrSWctfJRM/Dw2hqhk6bws24kt8lnJsSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQpoyL/btrSWctfJRM/Dw2hqhk6bws24kt8lnJsSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQpoyL/btrSWctfJRM/Dw2hqhk6bws24kt8lnJsSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQpoyL%2FbtrSWctfJRM%2FDw2hqhk6bws24kt8lnJsSk%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;2699&quot; height=&quot;647&quot; data-origin-width=&quot;2699&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2664&quot; data-origin-height=&quot;1330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m8ulq/btrSUHUWDjY/NTkzOGDbjA6nSE9ye4Ta50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m8ulq/btrSUHUWDjY/NTkzOGDbjA6nSE9ye4Ta50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m8ulq/btrSUHUWDjY/NTkzOGDbjA6nSE9ye4Ta50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm8ulq%2FbtrSUHUWDjY%2FNTkzOGDbjA6nSE9ye4Ta50%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;2664&quot; height=&quot;1330&quot; data-origin-width=&quot;2664&quot; data-origin-height=&quot;1330&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;b&gt;저장 후 빌드를 해보았습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3827&quot; data-origin-height=&quot;1549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5slY3/btrSVaQbtsw/vCKK7hjJ739O0gYkql0VK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5slY3/btrSVaQbtsw/vCKK7hjJ739O0gYkql0VK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5slY3/btrSVaQbtsw/vCKK7hjJ739O0gYkql0VK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5slY3%2FbtrSVaQbtsw%2FvCKK7hjJ739O0gYkql0VK0%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;3827&quot; height=&quot;1549&quot; data-origin-width=&quot;3827&quot; data-origin-height=&quot;1549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;branch 이름이 main으로 되어있어서 처음 시도는 실패하였고, master로 수정하여 성공하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;a href=&quot;https://junhyunny.github.io/information/jenkins/github/jenkins-github-webhook/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;젠킨스 Github Webhooks 연동&lt;/a&gt;&lt;/p&gt;</description>
      <category>CI.CD/Jenkins</category>
      <category>GIT</category>
      <category>jenkins</category>
      <author>lakelight</author>
      <guid isPermaLink="true">https://lakelight.tistory.com/130</guid>
      <comments>https://lakelight.tistory.com/130#entry130comment</comments>
      <pubDate>Tue, 6 Dec 2022 11:47:28 +0900</pubDate>
    </item>
  </channel>
</rss>