<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>생각하는 개발자</title>
    <link>https://anythingis.tistory.com/</link>
    <description>부득탐승</description>
    <language>ko</language>
    <pubDate>Thu, 9 Jul 2026 10:04:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>비오베베</managingEditor>
    <image>
      <title>생각하는 개발자</title>
      <url>https://tistory1.daumcdn.net/tistory/4253700/attach/e36bf62b987b4e72b9411f744aa7ee04</url>
      <link>https://anythingis.tistory.com</link>
    </image>
    <item>
      <title>오라클 성능고도화 1 - (Redo, Undo, 문장수준 읽기 일관성)</title>
      <link>https://anythingis.tistory.com/212</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redo&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오라클은 데이터 파일과 컨트롤 파일에 가해지는 모든 변경사항을 하나의 Redo 로그 엔트리로서 Redo 로그에 기록&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;Redo 로그 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Online redo 로그 : Redo 로그에 버퍼링된 로그 엔트리 기록, 최소 두개이상의 파일로 구성, 사용중인 Redo 로그 파일이 꽉차면 로그 스위칭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Archived Redo 로그 : Online Redo 로그가 재사용되기 전에 다른 위치로 백업해둔 파일&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;Redo 로그의 목적&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Database Recovery&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적으로 디스크 깨지는 Media Fail 발생시 데이터베이스 복구를 위해 Archived Redo 로그 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Cache Recovery&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Crash 발생 후 시스템을 재기동하면 Online Redo 로그에 저장된 기록을 읽어 마지막 체크포인트 이후부터 사고 발생 직전까지 수행된 트랜잭션 재현 =&amp;gt; Roll forward&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Roll forward : 버퍼 캐시에만 수정하고 데이터 파일에 반영되지 않았던 변경사항을 복구, 트랜잭션의 커밋여부를 불문하고 일단 버퍼 캐시를 시스템이 셧다운 되기 이전 상태로 되돌리는 것&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Fast Commit&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 메모리 버퍼 블록을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 디스크 상의 데이터에 블록에 기록하는 작업은 Random 액세스 방식으로 이루어져 느림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그는 Append 방식으로 기록하므로 상대적으로 매우 빠름&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;트랜잭션 발생시 건건이 데이터 파일에 기록하기 보다 변경사항을 Append 방식으로 빠르게 로그 파일에 기록하고 메모리 데이터 블록과 데이터 파일 간 동기화는 DBWR, Checkpint를 이용해 배치 방식으로 일괄 수행&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;Redo 레코드를 기록하는 과정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 사용자 커밋&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 서버 프로세스는 커밋 레코드를 Redo 로그 버퍼에 기록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. LGWR가 즉시 트랜잭션 로그 엔트리와 함께 Redo 로그 파일에 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 커밋을 수행한 서버 프로세스에 'success code' 리턴&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;i&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Log Force at Commit :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 4번 이후 DBWR 수행되어 디스크에 Dirty 블록 동기화 시키지만 Instance Crash가 발생하더라도 Redo를 통해 복구 할 수 있음&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Write Ahead Logging : DBWR가 버퍼 캐시로부터 Dirty 블록들을 디스크에 기록하기 전에 LGWR가 해당 Redo 엔트리를 모두 Redo 로그파일에 기록을 보장&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Undo&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Undo 세그먼트는 테이블 세그먼트와 아래와 같은 구조적 동일성을 가진다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 익스텐트 단위 확장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 빠른 읽기/쓰기를 위해 Undo 블록들을 버퍼 캐시에 캐싱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 데이터 유실을 방지하기 위해 변경사항을 Redo 로그에 로깅&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;Undo 세그먼트 목적&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Transaction Rollback&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 롤백 시 Undo 데이터 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Transaction Recovery&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Crash 발생 후 Redo를 이용해 Roll forward 단계가 완료되면 최종 커밋되지 않은 변경사항을 모두 복구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 셧다운 시점에 아직 커밋되지 않았던 트랜잭션을 롤백할 때 Undo 활용&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;3. Read Consistency&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Undo 데이터 활용해 읽기 일관성 제공&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;(1) Undo 세그먼트 트랜잭션 테이블 슬롯&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Undo 세그먼트의 첫번재 익스텐트 중 첫번째 블록에는 Undo 세그먼트 헤더 정보가 담긴다&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;Undo 세그먼트 헤더&lt;/b&gt;에는 &lt;b&gt;트랜잭션 테이블 슬롯&lt;/b&gt;이 위치하고, 슬록에 기록되는 사항은 아래와 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 트랜잭션 ID&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 트랜잭셩 상태정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 커밋 SCN&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Last UBA(Undo BLock Address)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 기타&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;i&gt;&lt;b&gt;먼저 Undo 세그먼트에 있는 트랜잭션 테이블로부터 슬롯을 할당받아야하고, 할당 받은 슬롯에 자신이 현재 Active 상태임을 표시하고 갱신 시&lt;/b&gt;&lt;b&gt;작&lt;/b&gt;&lt;/i&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;(2) 블록 헤더 ITL 슬롯&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 &lt;b&gt;데이터 블록과 인덱스 블록헤더&lt;/b&gt;에는 &lt;b&gt;ITL 슬롯&lt;/b&gt;이 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 볼록에 속한 레코드를 갱신하려면 먼저 블록 헤더로부터 ITL 슬롯을 확보해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ITL 슬록에 트랜잭션 ID를 기록하고 현재 해당 트랜잭션이 Active 상태임을 표시한후에 블록 갱신이 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ITL 슬롯 번호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 트랜잭션 ID&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. UBA&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 커밋 Flag&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Locking 정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 커밋 SCN&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;(3) Lock Buyte&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;블록에 레코드가 저장되는 로우마다 그 헤더&lt;/b&gt;에 Lock Byte를 할당해 해당 로우를 갱신 중인 트랜잭션의 ITL 슬롯 번호를 기록&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. 대상 레코드의 Locy Buyte가 활성화 시&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. ITL 슬롯을 찾아가고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 다시 ITL이 가리키는 트랜잭션 테이블 슬롯을 찾아가 트랜잭션이 active 상태면 트랜잭션이 완료 될 때 까지 대기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문장수준 읽기 일관성&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 문장수준 읽기 일관성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 SQL 문이 수행되는 도중 다른 트랜잭션에 의해 데이터의 추가 변경 삭제가 발생하더라도 일관성 있는 결과집합을 리턴하는 것&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;787&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k02hU/dJMcadob18C/KuHhBu7M4YOn7J0CPzv4k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k02hU/dJMcadob18C/KuHhBu7M4YOn7J0CPzv4k1/img.png&quot; data-alt=&quot;해당 케이스는 오라클에선 발생하지 않음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k02hU/dJMcadob18C/KuHhBu7M4YOn7J0CPzv4k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk02hU%2FdJMcadob18C%2FKuHhBu7M4YOn7J0CPzv4k1%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;787&quot; height=&quot;575&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해당 케이스는 오라클에선 발생하지 않음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) Consistent 모드 블록 읽기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CR 블록은 Current 블록의 복사본&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 시작 시 SCN 보다 큰 블록은 그 이전의 SCN 값으로 Undo를 통해 되돌려서 읽음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Undo가 롤백을 진행하지 못하면 Snapshot Too Old 발생&lt;/p&gt;</description>
      <category>오라클성능고도화</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/212</guid>
      <comments>https://anythingis.tistory.com/212#entry212comment</comments>
      <pubDate>Wed, 22 Apr 2026 20:16:43 +0900</pubDate>
    </item>
    <item>
      <title>오라클 성능 고도화 1-(DB 버퍼캐시, 버퍼 Lock)</title>
      <link>https://anythingis.tistory.com/211</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 버퍼캐시&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(1) 블록 단위 I/O&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오라클에서 I/O는 블록 단위로 이루어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;블록 단위로 읽는 다는 의미는, 하나의 레코드에서 하나의 컬럼만을 읽고자 하더라도 레코드가 속한 블록 전체를 읽게 됨을 의미한다. &lt;b&gt;SQL 성능을 좌우하는 가장 중요한 성능 지표는 액세스하는 블록 개수&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(2) 버퍼 캐시 구조&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4nhxA/dJMcaiCNgpI/LKtC2Rv8tvQYMNtmNUKV6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4nhxA/dJMcaiCNgpI/LKtC2Rv8tvQYMNtmNUKV6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4nhxA/dJMcaiCNgpI/LKtC2Rv8tvQYMNtmNUKV6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4nhxA%2FdJMcaiCNgpI%2FLKtC2Rv8tvQYMNtmNUKV6K%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;1155&quot; height=&quot;518&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DBA : DB 버퍼 캐시 내에서 데이터 블록을 해싱하기 위해 사용되는 키 값&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해시 함수에 DBA를 입력해 리턴 받은 해시 값이 같은 블록들을 해시 버킷의 해시 체인을 통해 구현&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 버퍼 액세스 순서&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775043893984&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. DBA 해싱
2. 해시 버킷 액세스 
3. 해시 체인(버퍼 헤더) 액세스
4-1. 없으면 File Read 후 해시 체인에 연결 후 읽음
4-2. 있으면 바로 읽음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(3) 캐시 버퍼 체인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 버퍼 캐시는 공유 메모리 영역인 SGA 내에 존재하므로 여러 프로세스에 의한 각 &lt;b&gt;해시 체인&lt;/b&gt;에 대한 동시 액세스를 Latch를 통해 직렬화 한다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; letter-spacing: 0px;&quot;&gt;래치를 획득한 프로세스만이 그 래치에 의해 보호되는 자료구조로의 진입이 허용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 그림 처럼 chache bufferes chains 래치 : 해시체인은 1:N 구조이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; 데이터 자체 보호가 아닌 &lt;b&gt;SGA 공유된 자료구조를 보호하는 것&lt;/b&gt;이고 해시 체인 스캔하거나 블록 추가, 제거 시 래치가 요구된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용 작업 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(select 문이 아닌 해시 체인 스캔하며 블록찾는 작업)&lt;span&gt; &lt;/span&gt;&lt;/span&gt;일 때는 Share 모드획득하지만, 그 외의 작업은 래치 경합이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(4) 캐시 버퍼 LRU 체인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;버퍼 헤더는 해시 체인 뿐 아니라 LRU 체인에 의해서도 연결돼 있다. &lt;b&gt;버퍼 블록 헤더&lt;/b&gt;를 사용빈도순으로 위치를 옮겨가다가, Free 버퍼가 필요해질 때마다 액세스 빈도가 낮은 데이터 블록들을 우선하여 밀어낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리하자면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 버퍼 체인은 &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775044006091&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. cache buffers chains 래치을 통해 SGA의 액세스를 직렬화하고
2. cache bufferes lru chains 래치를 통해 자주 사용하는 블록을 SGA에 보존한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;버퍼 Lock&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(1) 버퍼 Lock(Pinned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아주 짧은 순간일지라도 두개 이상의 프로세스가 동시에 버퍼 내용을 읽고 쓰는 것을 막기 위해 프로세스는 먼저 버퍼 헤더로부터 버퍼 Lock을 획득해야 한다. 보통은 &lt;b&gt;래치 획득을 두번&lt;/b&gt;하지만, 몇몇 오퍼레이션에선 래치를 쥔채 버퍼 블록을 읽는 경우도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775044624956&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 래치 획득
2. 타 프로세스가 버퍼 Lock 획등 중
3. 버퍼 Lock Waiter List 등록(buffer busy waits event 발생)
4. 래치 해제
5. 타 프로세스 버퍼 Lock 해제 시 버퍼 Lock 획득
6. 데이터 읽고 쓰기
7. 래치 획득
8. 버퍼 Lock 해제
9. 래치 해제&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(2) 버퍼 핸들&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;버퍼 Lock을 설정하는 것은 자신이 현재 그 버퍼를 사용중임을 표시해두는 것. 버퍼 핸들도 공유된 리소스이므로 또 다른 래치인 cache buffer handles 래치 사용 버퍼를 Pin하는 오페레이션이 많은 수록 cache buffer nadles 래치가 경합지점이 되기 떄문에 프로세스 당 _db_handles_cached 개수만큼 버퍼 핸들을 미리 할당한다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;읽기 작업을 위해서라면 여러 개 프로세스가 동시에 Pin 설정이 가능하지만, &lt;b&gt;변경 시에는 하나의 프로세스만 Pin 설정&lt;/b&gt;이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(3) 버퍼 Lock의 필요성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DML Lock과 별개로 Block 단위 Lock을 걸기위해 존재한다. 로우가 달라도 블록은 같은 경우도 존재하고, 이럴 때 두개의 프로세스가 동시에 블록에 접근하면 데이터 정합성이 깨지기 때문에 이를 버퍼 Lock으로 직렬화한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(4) 버퍼 Pinning&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터베이스 Call 진행 시 버퍼 Pinning 유지 시 같은 블록 읽을 때 래치 획득 과정을 생략을 통해 논리적 블록 읽기 횟수를 감소시킨다. 재방문 가능성이 큰 오퍼레이션을 수행할 때만 사용한다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775046141728&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 인덱스 리프 블록 Pin 이후 테이블 블록 방문
2. 인덱스 클러스터링 팩터가 좋으면 테이블 블록에 Pin
3. NL Join 시 Inner Table Index Root 블록에 Pin
4. Index Skip Scan 시 브랜치 블록에 Pin
5. DML 수행 시 Undo 블록 Pin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000061696047&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://product.kyobobook.co.kr/detail/S000061696047&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775046360120&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;오라클 성능 고도화 원리와 해법 1 | 조시형 - 교보문고&quot; data-og-description=&quot;오라클 성능 고도화 원리와 해법 1 | 초 대용량 데이터베이스 환경의 여러 차세대 시스템 구축 프로젝트에서 얻은 소중한 경험과 기술 노하우를 널리 전파하기 위해 만든 책이다. 데이터베이스 &quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000061696047&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000061696047&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/XkcSJ/dJMb8SXx29x/URkwRz8SYEA3yz27H1U7c1/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/Toi5L/dJMb9kT3fJd/1v7u1Zjb6KbkhJLSyv3470/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/q2Zmc/dJMb8Rj2bYy/wa7CsaCgkCro9E97TnphI1/img.jpg?width=599&amp;amp;height=608&amp;amp;face=0_0_599_608&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000061696047&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000061696047&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/XkcSJ/dJMb8SXx29x/URkwRz8SYEA3yz27H1U7c1/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/Toi5L/dJMb9kT3fJd/1v7u1Zjb6KbkhJLSyv3470/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/q2Zmc/dJMb8Rj2bYy/wa7CsaCgkCro9E97TnphI1/img.jpg?width=599&amp;amp;height=608&amp;amp;face=0_0_599_608');&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;오라클 성능 고도화 원리와 해법 1 | 조시형 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오라클 성능 고도화 원리와 해법 1 | 초 대용량 데이터베이스 환경의 여러 차세대 시스템 구축 프로젝트에서 얻은 소중한 경험과 기술 노하우를 널리 전파하기 위해 만든 책이다. 데이터베이스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&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;</description>
      <category>도서/오라클성능고도화</category>
      <category>DB 버퍼캐시</category>
      <category>oracle</category>
      <category>버퍼 Lock</category>
      <category>오라클성능고도화</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/211</guid>
      <comments>https://anythingis.tistory.com/211#entry211comment</comments>
      <pubDate>Wed, 1 Apr 2026 21:26:23 +0900</pubDate>
    </item>
    <item>
      <title>되고자 하는 개발자의 방향성</title>
      <link>https://anythingis.tistory.com/210</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요즘 개발직군이 아니더라도 AI에 대체된다는 말이 굉장히 많습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순히 먼 미래라고 하기엔 미국의 빅테크부터 우리나라의 전문직들에 대한 정보도 종종 접하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.news21tongsin.co.kr/news/285125&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.news21tongsin.co.kr/news/285125&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774960989672&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;마이크로소프트(MS), 9000명 해고 발표. . .AI로 대량 해고가 현실화&quot; data-og-description=&quot;IT 기업들이 몰려 있는 판교 테크노밸리(사진=네이버 db 갈무리)[뉴스21 통신=추현욱 ] 인공지능(AI)으로 인한 대량 해고가 현실화됐다.국내외 안팎에서 대규모 희망퇴직이 잇따르고 있다. &amp;lsquo;중국&quot; data-og-host=&quot;www.news21tongsin.co.kr&quot; data-og-source-url=&quot;https://www.news21tongsin.co.kr/news/285125&quot; data-og-url=&quot;http://www.news21tongsin.co.kr/news/285125&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjzTpt/dJMb9iIHfYi/kWEMCZ20ybrVn2zDf6RScK/img.jpg?width=650&amp;amp;height=434&amp;amp;face=0_0_650_434,https://scrap.kakaocdn.net/dn/bFqRke/dJMb9iIHfYh/SK1M5djoaP240kA91OMGm0/img.jpg?width=650&amp;amp;height=434&amp;amp;face=0_0_650_434,https://scrap.kakaocdn.net/dn/cc8nU9/dJMb9eTPQhf/ph1jqQCfwLZ0UxkrfQesn1/img.jpg?width=650&amp;amp;height=434&amp;amp;face=0_0_650_434&quot;&gt;&lt;a href=&quot;https://www.news21tongsin.co.kr/news/285125&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.news21tongsin.co.kr/news/285125&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjzTpt/dJMb9iIHfYi/kWEMCZ20ybrVn2zDf6RScK/img.jpg?width=650&amp;amp;height=434&amp;amp;face=0_0_650_434,https://scrap.kakaocdn.net/dn/bFqRke/dJMb9iIHfYh/SK1M5djoaP240kA91OMGm0/img.jpg?width=650&amp;amp;height=434&amp;amp;face=0_0_650_434,https://scrap.kakaocdn.net/dn/cc8nU9/dJMb9eTPQhf/ph1jqQCfwLZ0UxkrfQesn1/img.jpg?width=650&amp;amp;height=434&amp;amp;face=0_0_650_434');&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;마이크로소프트(MS), 9000명 해고 발표. . .AI로 대량 해고가 현실화&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;IT 기업들이 몰려 있는 판교 테크노밸리(사진=네이버 db 갈무리)[뉴스21 통신=추현욱 ] 인공지능(AI)으로 인한 대량 해고가 현실화됐다.국내외 안팎에서 대규모 희망퇴직이 잇따르고 있다. &amp;lsquo;중국&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.news21tongsin.co.kr&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=7AWHmCQq35Q&amp;amp;t=124s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=7AWHmCQq35Q&amp;amp;t=124s&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=7AWHmCQq35Q&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/jmfBg/dJMb85vO8Ul/XBRRGB1u1ruiKikImxkMy1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/e7UTK/dJMb84p8QSG/XDp51208ILZAA9LLHnSok0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/DJ5uC/dJMb87f6DS6/cGYwkF6z5gh05h2MXkbGTK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;&amp;quot;이력서 100개 넣었는데 떨어졌어요&amp;quot; 회계사, 개발자... 각광 받던 직업들이 당장 AI로 대체되고 있&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/7AWHmCQq35Q&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;IT 개발자는 오프라인의 작업을 효율적으로 처리하는 방법을 찾아온 직군이고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AI라는 도구를 잘 써서 어떻게 발전할지에 대해서 잘 고민해보는게 앞으로의 방향성이라고들 말합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;제가 생각을 해보고 내린 결론은 &lt;b&gt;어떤 도메인이든 빠르게 적응하고, 그에 맞는 설계&lt;/b&gt;를 할줄 아는 사람이 되는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;솔루션 개발에 대한 고민&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저희 회사도 나름 도메인에 대한 솔루션을 구축하고, 내부 프레임워크화를 통해 이 기술에 익숙해진 사람이 어떤 기능을 구현하는 데 생산성이 좋다고 생각합니다. 하지만 B2B 도메인 특성상 설계된 데이터 모델링 기반에서 도메인 업무를 구현하는 정도의 요구사항이 대부분이여서 대용량 데이터와는 거리가 멀기에 성능, 동시성에 대한 설계는 찾아보기 힘들고(업무 채번 하는 정도) 가끔 네트워크 지연이나 에러로 인해 데이터에 이상현상이 발생하는 경우도 모니터링되는게 아니라 고객이 이상함을 느끼고 문의를 하면 데이터 보정으로 처리하는 프로세스입니다. 회사 입장에서는 이렇게 운영되는게 더 정답일 수 있습니다. 오버 엔지니어링을 통해 프로젝트 기간 내 공수를 오버하거나 유지보수가 힘들 수 있으니까요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 고객사의 ERP나 그룹웨어와 연동할 때도 SAP JCO, API, 파일 전송, 중계 DB 등의 방식을 통해 연동되고, 만약 해당 요청이 실패했을 경우에 재처리할껀지, 취소할껀지에 대한 설계 없이 데이터가 이상하다면 보정을 통해 운영되고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 오픈된지 7년된 서비스이지만, 테스트에 관련된 클래스도 없고, CI/CD가 구축되어 있지만 Maven 빌드시 &lt;span style=&quot;background-color: #f3f5f6; color: #0a0a0a; text-align: start;&quot;&gt;-DskipTests=true가 켜진 상태로 배포되고, 기능이 변경되면 사람이 전수 테스트를 하고, 테스트 하는 범위도, 기준도 애매합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;제가 일을 할 때도 성능을 고려하지 않고 요구사항을 구현만 하면 되니까 지금까지 일정이 빡빡한게 아닌 경우에 기술적으로 어려움을 느낀적은 없습니다. 아직 연차가 얼마되지 않아 그렇게 느낄 수도 있지만 회사 선배님들을 보면 연차가 쌓여도 기술적인 이해보다는 도메인에 대한 이해도가 높아지면서, 고객사와 커뮤니케이션 하는 역량이 발달되는 것 같은 느낌을 받습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인과 기술&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;110&quot; data-start=&quot;81&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인과 기술에 대해서 고민할 때 생각해보게 되는 건&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;157&quot; data-start=&quot;112&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;누군가는 이제 기술은 AI가 맡고, 도메인 역량을 키워야 한다고 말하기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;157&quot; data-start=&quot;112&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;181&quot; data-start=&quot;161&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 저는 조금 다르게 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;247&quot; data-start=&quot;183&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;항공모함을 몰 줄 아는 선장은 통통배를 몰게 되었을 때 금방 적응할 수 있지만, 그 반대는 쉽지 않다고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;358&quot; data-start=&quot;249&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결국 기술적인 이해도가 높은 사람은 새로운 도메인을 접하더라도 구조를 빠르게 파악하고 적응할 수 있고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;더 복잡하고 규모가 큰 도메인도 상대적으로 어렵지 않게 이해할 수 있다고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;358&quot; data-start=&quot;249&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;503&quot; data-start=&quot;362&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;회사의 관리자 중에 많은 분들은 지금 회사의 서비스보다 규모가 작은 곳에서 이직을 하신 분들이 더 많을 거라고 생각합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그러면서 점점 지금 회사가 규모가 큰 설계로 나아가기보다, 짬바로 쌓이는 도메인 역량을 통해 운영되고 있는 상태라고 느낍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;678&quot; data-start=&quot;507&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실무를 하다 보면 누구나 도메인 역량은 자연스럽게 쌓입니다. 하지만 이런 환경에서는 서비스 규모가 커질 때 필요한 설계나 기술적인 이해는 따로 공부하지 않으면 성장하기 어렵다고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;678&quot; data-start=&quot;507&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 이런 기술적인 기반이 부족하면, 새로운 도메인을 이해하는 속도나 깊이에서도 한계가 생긴다고 느꼈습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;736&quot; data-start=&quot;682&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링을 공부할 때 많은 개발자들이 Spring Core와 Spring MVC를 헷갈리기도 하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;808&quot; data-start=&quot;738&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 시큐리티 역시 필터 기반으로 동작하지만, 필터와 매니저 간의 관계를 정확히 이해하지 못한 채 사용하는 경우도 많습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;860&quot; data-start=&quot;810&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;심지어 쿼리를 짤 때도 인덱스 선두 컬럼과 쿼리의 조건절 순서와는 관련이 없다는 사실처럼, 이런 부분들은 단순히 사용만 해서는 알기 어렵습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;891&quot; data-start=&quot;862&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;997&quot; data-start=&quot;895&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결국 사용하는 기술이 추상화될수록 그 내부 구현에 대한 이해도가 높은 사람이 더 빠르게 도메인을 이해하고, 더 큰 규모의 문제도 다룰 수 있는 무기를 가지게 된다고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;997&quot; data-start=&quot;895&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;997&quot; data-start=&quot;895&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 걸 공부해야하나?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;997&quot; data-start=&quot;895&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현재 계속 발전하는 AI 기술과 그 외의 기술(DB, Java, 시스템 설계) 중 어떤걸 공부해야하나에 대한 고민이 많습니다. 제 생각은 취업을 하려면 후자쪽이 더 유리하고, 뛰어난 분들의 기술블로그를 보면서 어떤걸 공부하냐보다 &lt;b&gt;어떤 것이든 어떻게 공부하는 게 중요하다는걸&lt;/b&gt; 배웠습니다. 일단 앞으로 3개월정도는 SQLP 시험전에 짬짬히 공부하던 오라클에 대한 아키텍쳐와 데이터 모델링에 대한 부분을 깊게 파보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;2137&quot; data-start=&quot;2130&quot; data-section-id=&quot;1xgz7x2&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마무리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;158&quot; data-start=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB에 대한 공부와 시스템 설계에 대한 부분을 조금 더 의식적으로 가져가려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;158&quot; data-start=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;160&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우선 Oracle 기반에서 실행계획을 직접 분석하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스 설계나 쿼리 개선을 통해 실제 성능을 개선해보는 경험을 쌓으려고 합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순히 동작하는 SQL이 아니라, 왜 이 쿼리가 이렇게 실행되는지까지 이해하는 방향으로 공부해보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;160&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;408&quot; data-start=&quot;301&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 시스템 설계 측면에서도&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지금 환경에서는 많이 접하기 어려웠던 동시성 처리나 트랜잭션, 장애 상황을 고려한 구조를 따로 정리하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;작게라도 직접 설계해보는 연습을 해보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;408&quot; data-start=&quot;301&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;509&quot; data-start=&quot;410&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런 과정들을 쌓아가면서 단순히 요구사항을 구현하는 개발자를 넘어서,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조금 더 큰 규모에서도 통할 수 있는 설계를 해볼 수 있는 환경으로 가는 것을 목표로 하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>AI</category>
      <category>개발자</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/210</guid>
      <comments>https://anythingis.tistory.com/210#entry210comment</comments>
      <pubDate>Tue, 31 Mar 2026 23:23:53 +0900</pubDate>
    </item>
    <item>
      <title>SQL 튜닝이 됬는지는 실행시간으로 비교하는 것이 아니다.</title>
      <link>https://anythingis.tistory.com/209</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오늘 사내 지라를 보다가 다른 팀원이 처리한 데이터 조회가 오래걸린다는 사용자 문의를 보고 포스팅합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이슈&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 지라는 담당자가 SQL과 Function, Table DDL 정보를 넘겨 튜닝을 했고, 그 결과는 아래와 같다고 기재되어있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 96.0466%; height: 39px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AS-IS&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3208%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;TO-BE&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 22px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실행시간&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 22px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5sec&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3208%; height: 22px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;0.5sec&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 표를 봤을 때 튜닝 이후에 속도가 무려 10배나 빨라졌지만, 운영에 반영되었을 때 해당 문제가 해결되지 않을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;무엇이 문제인가?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;튜닝이 되었는지는 이전 쿼리와 튜닝 쿼리의 실행시간으로 비교하는 것이 아닙니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL 튜닝을 하면 실행시간은 빨라져야하는것아닌가? 라고 생각하는게 결론적으로 맞는 얘기이지만 &lt;i&gt;&lt;b&gt;비교하는 대상이 '시간'이 되면 안된다는 뜻&lt;/b&gt;&lt;/i&gt;입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현재 사내 DB인 Oracle을 기준으로(다른 DB도 동작방식은 비슷하다고 알고있습니다.) SQL을 실행하면 하드디스크에 있는 데이터 파일을 DB 버퍼캐시(메모리)에 올린 뒤에 사용자에게 데이터를 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUO0tS/dJMcaiQg6WM/SZlcl4nXR3KY7Xt4npmSUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUO0tS/dJMcaiQg6WM/SZlcl4nXR3KY7Xt4npmSUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUO0tS/dJMcaiQg6WM/SZlcl4nXR3KY7Xt4npmSUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUO0tS%2FdJMcaiQg6WM%2FSZlcl4nXR3KY7Xt4npmSUk%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;738&quot; height=&quot;470&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;470&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;761&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nklny/dJMcaf0k25p/9oyJOaueVtZYCHoorGea80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nklny/dJMcaf0k25p/9oyJOaueVtZYCHoorGea80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nklny/dJMcaf0k25p/9oyJOaueVtZYCHoorGea80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNklny%2FdJMcaf0k25p%2F9oyJOaueVtZYCHoorGea80%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;761&quot; height=&quot;457&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 이후 동일한 데이터를 요청하는 SQL이 온다면 캐싱되어있는 데이터를 전달하기 때문에 만약 비교를 이전 쿼리 실행 이후 튜닝 쿼리를 실행한 시간을 비교한다면 실제로 튜닝이 잘 되지 않았음에도 불구하고 디스크에 있는 데이터가 아닌 메모리에 있는 데이터를 읽어오기 때문에 실행시간이 빨라질 수 있다는 점입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼 어떤걸 비교해야하는가 ?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL 튜닝은 모든 데이터를 물리적으로(하드디스크) 읽어온다고 가정하고 전체적인 일량이 줄어드는 것을 확인해야합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이는 실행계획을 통해서 확인할 수 있는데 가급적 예상 실행계획이 아닌 실제로 쿼리를 실행하고, 해당 쿼리의 실행계획을 통해서 전체적인 일량이 줄었는지 확인해야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Oracle에서는 아래와 같은 도구를 통해서 확인할 수 있고, 쿼리를 통해 가장 확인하기 쉬운 것은 1번 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. DBMS_XPLAN.DISPLAY_CURSOR&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774873823402&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT /*+ GATHER_PLAN_STATISTICS */
       *
FROM employees
WHERE employee_id = 100;

SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')); 
-- 해당 함수의 파라미터를 NULL, NULL로 넣는 것은 가장 최근에 실행된 쿼리에 대한 실행계획을 호출합니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. AUTOTRACE &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. SQL Trace &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Database</category>
      <category>oracle</category>
      <category>sql튜닝</category>
      <category>개발</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/209</guid>
      <comments>https://anythingis.tistory.com/209#entry209comment</comments>
      <pubDate>Mon, 30 Mar 2026 21:32:58 +0900</pubDate>
    </item>
    <item>
      <title>취업한지 1년 반 지난 개발자의 회고(요즘 생각, AI)</title>
      <link>https://anythingis.tistory.com/208</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;안녕하세요, 저는 1년 반정도 솔루션 기반의 프로젝트 위주로 개발을 해온 개발자입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;미래에 대한 고민 때문에 잠이 오지 않아 글을 써봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자란 무엇인가?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우선 취업하고부터 블로그에 글을 쓰는 횟수가 현저히 줄어든 것 같습니다. 회사생활에 치여서 그럴 수도 있겠지만 취준생 때는 개발을 통해 서비스를 만드는 것에 재미를 느끼고, 뭔가 개발자라면 퇴근하고도 개발을 공부해야 한다는 생각을 갖고 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 돈을 벌다보니, 인생의 전부를 개발로 채우는 것보다 개발로 돈을 벌고, 그 외의 시간에 내가 하고 싶은 취미를 하는 사람도 많고, 저도 지금까지 운동이나 피아노를 배우면서 이런 삶도 괜찮다는 걸 느꼈...는데 요즘 빨간불이 삐용삐용거립니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지금 회사는 B2B 기반의 Saas 솔루션 회사이고, 주로 거의 모든 요구사항은 도메인에 대한 이해를 통해 데이터 변경을 하는 것에 그쳐서 딱히 기술적인 한계를 맞닿드린적은 없고, 쿼리에 대한 부분이 많이 중요하고, 백엔드와 프론트엔드는 내부 프레임워크를 사용합니다. 그래서 입사했을 때부터 회사에서도 도움이 되면서 기술적으로 인정을 받을 수 있는 부분이 SQLP 취득인 것 같아서 공부중입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자로서 자격증이 무엇이 중요하나라고 생각하는 분들도 많지만, 자기객관화를 해봤을 때 면접이나 자소서같은걸 되돌아보면 저는 제 역량을 남들에게 전달하는게 부족하다고 생각했고 이런 부담을 조금이나마 내려놓을 수 있는게 실용성있는 자격증이라 생각해서 SQLP 취득을 위해서 공부중입니다. 비용기반 옵티마이저의 사고방식을 배우면서 오라클의 아키텍쳐가 어떻게 되는지, DB의 통계를 기반으로 쿼리의 실행계획을 봤을 때 인덱스와 풀스캔중 어떤게 유리한지에 대해 생각하는 법을 배우면서 기존 운영되는 쿼리를 제 맘대로 바꿀 수는 없지만, 제가 새롭게 기능구현 하는 부분에 대해서는 한번 더 고민하는 법을 배우게 된 것 같습니다. 하지만 일년에 두번있는 SQLP에 대해서 그다지 간절하지 않았던 건지.. 시험볼때마다 어떤 계획을 갖고 공부하기보다는 그냥 되는대로 공부를 하다보니 자신감은 떨어지고, 이정도면 됬겠지.. 하는 마음가짐으로 봐서 계속 낙방중입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아마 다음에 시험보기전에는 지금 약점인 SQLD 부분을 잘채우고, 오라클 성능고도화를 전체적으로 한번 훑어보면서 특히 파티셔닝과 병렬처리에 대한 개념을 완벽히 이해하고, 실기를 바라볼 때 자격검정 문제집에 있는 풀이를 맞추는 게 중요한게 아니라 같은 문제도 다른 환경이 주어질 때(부분범위 처리, 파티셔닝 유무, 인덱스 구성)에 대한 고민과 실제 문제처럼 단일 개념이 아니라 여러 개념을 묶어서 생각해보는 걸 연습해볼 것 같습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AI란..?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오히려 계속 떨어지다보니 지금 유행하는 AI Agent에 대해서 공부할 틈이 없다고 자책중인데, 이것도 사실 시간내서 공부하면 되는 부분일 것 같습니다. 요즘 회사에서는 클로드코드, 커뮤니티에서는 오픈클로 같은게 유행인데 아직 사용해보지 않아서 .. 작년 추석 때도 바이브코딩이 유행해서 커서를 활용해 플러터로 앱을 만들어 구글 플레이스토어에 출시해보려고했는데 2주간 여러 테스터들을 구하고 품앗이 하는 것까진 진행했었는데 테스트에 통과하지 못했고, 이게 다시 테스터를 구할 정도의 생각은 없어서 그만뒀었습니다. 그 후로 고작 반년 지났는데 지금은 클로드코드, 오픈클로 세상이 온걸보니 진짜 유행이 확확 변하는 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개인적인 생각은 요즘 회사에서도 AI를 꼭 써야하고, 이걸 사용해서 성과를 내라는 KPI가 많아지는 것 같고 사람들은 어떤 것이든 해결할 수 있는 '치트키'라고 생각한다고 느꼈습니다. 커서를 통해 플러터 앱을 만들고 느낀점은 '커서 쓰는 방법은 알았는데 생산적인 앱을 만드려면 플러터를 공부해야겠다'였습니다. 플러터의 라이프사이클을 모르고, 전체적으로 코드들이 어떤 부분에서 어떻게 사용되는지 모르니 결과물은 나왔지만 이게 내 앱이라고 할 수 있나...? 라는 생각이 들었습니다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;물론 AI를 활용해서 생산성을 늘리는 것은 중요하지만 결국 전체적인 비즈니스 관점에서 AI가 말하는게 우리 서비스의 비즈니스 관점과 일치하는지, 확장 가능성은 있는지 판단하는 사람은 있어야한다고 생각합니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하고 싶은게 뭔가 ?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지금 회사에 취업하기전에도 도메인이 재밌다고 생각했는데, 지금은 굉장히 폐쇄적인 도메인이라는 생각이 듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;취준생일 때는 아직 하고 싶은게 명확하지 않았고, 이런 생각은 회사를 다니면 하고 싶은데 명확해지지 않을까? 생각했는데 현실은 열심히 살면서 최대한 많은 기업에 지원해 면접까지라도 가보면서 나와 맞는게 무엇인지 찾아야 된다고 느꼈습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;무엇보다 하고싶은게 뭔지에 대한 물음은 어느날 찾아오는 깨달음이 아니라 계속 이것저것 꾸준히 해봐야되는 것 같습니다. 지금 취미로 하는 피아노도 배운지 4개월째야 코드를 좀 외우면서 재미를 느꼈고, 남들 다하는 ISA에 나스닥100만 사뒀다가 작년 4월 트럼프 관세빔 맞고 -20프로 까지 빠져서 앱을 켜보지도 않다가도 뉴스에서 상법개정을 줏어듣고 어릴적부터 팬이였던 '한화' 지주사를 사보고 굉장히 오르면서 주식에 대한 흥미가 생긴 것처럼요..?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;글을 잘쓰자&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요즘 여러 개발자들의 블로그를 보면서 느낀건 '글만 봐도 잘하는 사람은 알 수 있겠다' 였습니다. 이게 뭐가 다른건지 아직 파악은 못했지만 글을 쭉쭉 읽고 이 사람 잘한다고 느끼면 보통 거대서비스업을 다니는 분들이였습니다. 여기서 느낀건 저는 정보를 집어넣기만(강의를 듣든, 책을 읽든)하고 그것에 대한 정리는 어떻게든 한 뒤 아웃풋은 뭐 언젠가 하자는 생각을 갖고 산 것 같습니다. 하지만 목표가 있는 분들은 인풋에 대해서 남들에게 설명할 수 있을 정도로 체계화를 시키고 끊임없이 목표를 실현시킬 수 있는 환경을 찾고자 하는게 다르다고 느꼈습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보통 서비스 회사들이 요구하는 어떤걸 몰입해본 경험... 을 되돌아보면 서류 쓰기부터 막막한데 다음주부터 다시 SQLP든, 자바든, 설계에 대한 부분이든 조금씩 블로그에 새롭게 얻는 정보들을 정리하다보면 더 나은 미래를 위해 싸우는 개발자가 되지 않을까 ..? 싶습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 요즘 많은 블로그를 보면서 느끼는건 AI가 유행하는 이 시대에 사람을 끌어들이는 건 AI가 아닌 사람의 감성이 들어간 글인 것 같습니다. 정보를 얻는 것 외에는 최대한 AI를 덜 써보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1774793525335&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;주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 | 최범균 - 교보문고&quot; data-og-description=&quot;주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 | 실무에서 자주 겪는 다양한 문제를 효과적으로 해결하는 법서비스 환경에서는 커넥션을 닫지 않아 서버가 멈추고 외부 API의 지연이 전체 &quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000216376461&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000216376461&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yVV6N/dJMb88F4St1/N0Jikl3vBUhv0OH7RgHv21/img.jpg?width=458&amp;amp;height=588&amp;amp;face=0_0_458_588,https://scrap.kakaocdn.net/dn/IrOXf/dJMb81GXc1N/h3xxnB5N34hEkY6VCXPPQ0/img.jpg?width=458&amp;amp;height=588&amp;amp;face=0_0_458_588,https://scrap.kakaocdn.net/dn/fc5wc/dJMb88F4St0/kp66nTGVWhlJRQS0Tkyk10/img.jpg?width=599&amp;amp;height=608&amp;amp;face=0_0_599_608&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216376461&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000216376461&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yVV6N/dJMb88F4St1/N0Jikl3vBUhv0OH7RgHv21/img.jpg?width=458&amp;amp;height=588&amp;amp;face=0_0_458_588,https://scrap.kakaocdn.net/dn/IrOXf/dJMb81GXc1N/h3xxnB5N34hEkY6VCXPPQ0/img.jpg?width=458&amp;amp;height=588&amp;amp;face=0_0_458_588,https://scrap.kakaocdn.net/dn/fc5wc/dJMb88F4St0/kp66nTGVWhlJRQS0Tkyk10/img.jpg?width=599&amp;amp;height=608&amp;amp;face=0_0_599_608');&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;주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 | 최범균 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 | 실무에서 자주 겪는 다양한 문제를 효과적으로 해결하는 법서비스 환경에서는 커넥션을 닫지 않아 서버가 멈추고 외부 API의 지연이 전체&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;너무 두서없이 쓴 글이지만 앞으로 글솜씨는 점점 발전하지 않을까 싶습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>개발자</category>
      <category>요즘생각</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/208</guid>
      <comments>https://anythingis.tistory.com/208#entry208comment</comments>
      <pubDate>Sun, 29 Mar 2026 23:41:03 +0900</pubDate>
    </item>
    <item>
      <title>Oracle RDS 대용량 데이터 삭제시 주의할 점</title>
      <link>https://anythingis.tistory.com/205</link>
      <description>&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;회사 서비스의 로그는 서비스 내부에서 다음과 같은 목적으로 저장되고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;434&quot; data-start=&quot;325&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;345&quot; data-start=&quot;325&quot; data-section-id=&quot;uf77ds&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자의 행동이 발생할 때마다 OR&amp;nbsp;내부 서비스와 외부 ERP / API 인터페이스 호출마다&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;434&quot; data-start=&quot;382&quot; data-section-id=&quot;1jt0jbw&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Controller &amp;rarr; Service &amp;rarr; DAO 흐름 단위의 로그 데이터를 DB에 저장&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;496&quot; data-start=&quot;436&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 관리자가 필요할 때 &lt;b&gt;에러를 검색할 수 있는 관리 페이지&lt;/b&gt;에서 조회할 수 있도록 설계되어 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;539&quot; data-start=&quot;498&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제는 실제 운영 상황에서는 이 데이터를 거의 사용하지 않는다는 점이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;539&quot; data-start=&quot;498&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;558&quot; data-start=&quot;541&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제 운영 모니터링은 대부분 &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DataDog, &lt;/b&gt;&lt;/span&gt;&lt;b&gt;Whatap &lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;같은 APM 도구를 통해 이루어지고 있었고, DB 로그 테이블은 사실상 &lt;b&gt;방치된 상태&lt;/b&gt;였다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;게다가 이 서비스는 오픈된 지 &lt;b&gt;8년이 넘었지만 단 한 번도 삭제 작업이 이루어지지 않았다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;642&quot; 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;303&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VfzLq/dJMcacI6Fp8/5G6KkdrVoTaDkoGH1skNh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VfzLq/dJMcacI6Fp8/5G6KkdrVoTaDkoGH1skNh0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VfzLq/dJMcacI6Fp8/5G6KkdrVoTaDkoGH1skNh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVfzLq%2FdJMcacI6Fp8%2F5G6KkdrVoTaDkoGH1skNh0%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;303&quot; height=&quot;166&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;642&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;그 결과... 이 운영 DB 용량의 50% 이상이 쓸모없는 로그성 데이터였다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 data-end=&quot;723&quot; data-start=&quot;703&quot; data-section-id=&quot;1cb1t6b&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h3 data-end=&quot;723&quot; data-start=&quot;703&quot; data-section-id=&quot;1cb1t6b&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RDS 환경에서 커지는 비용 문제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;751&quot; data-start=&quot;725&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB는 &lt;b&gt;AWS RDS&lt;/b&gt;를 사용하고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;838&quot; data-start=&quot;753&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RDS의 특징 중 하나는 &lt;b&gt;스토리지 자동 확장&lt;/b&gt;이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터가 늘어나면 자동으로 저장 공간이 증가하고, 그만큼 &lt;b&gt;운영 비용도 계속 증가&lt;/b&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;842&quot; data-start=&quot;840&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉,&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;908&quot; data-start=&quot;844&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;861&quot; data-start=&quot;844&quot; data-section-id=&quot;1oxslff&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용하지 않는 로그 데이터가&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;880&quot; data-start=&quot;862&quot; data-section-id=&quot;1ct8e6e&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 용량의 대부분을 차지하고&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;908&quot; data-start=&quot;881&quot; data-section-id=&quot;1nlq1ot&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그로 인해 &lt;b&gt;운영 비용까지 증가하는 상황&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;914&quot; data-start=&quot;910&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이었다. 따라서 로그 데이터를 정리하는 작업이 필요했다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;956&quot; data-start=&quot;949&quot; data-section-id=&quot;1gg25xi&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;삭제 전략&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;994&quot; data-start=&quot;958&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터를 줄이는 방법으로는 다음과 같은 방법도 고려할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1020&quot; data-start=&quot;996&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;새로운 인스턴스를 만들고 마이그레이션&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1058&quot; data-start=&quot;1022&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 운영 중인 서비스에서 이 작업을 수행하기에는 부담이 컸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1115&quot; data-start=&quot;1060&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한 로그 데이터는 고객사가 필요할 경우 조회할 수 있어야 했기 때문에 완전히 제거할 수는 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1172&quot; data-start=&quot;1117&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 내부 정책에 따라 &lt;b&gt;데이터 보존 기간을 설정&lt;/b&gt;하고 다음과 같은 방식으로 정리하기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1227&quot; data-start=&quot;1174&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1201&quot; data-start=&quot;1174&quot; data-section-id=&quot;1n7bt3z&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보존 기간보다 오래된 데이터를 일괄 삭제&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1227&quot; data-start=&quot;1202&quot; data-section-id=&quot;1s8gwjk&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후에는 스케줄러를 통해 주기적으로 삭제&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-end=&quot;1260&quot; data-start=&quot;1234&quot; data-section-id=&quot;7oz7js&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아무 생각 없이 DELETE 했다가 생긴 일&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1277&quot; data-start=&quot;1262&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773228747037&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE FROM LOG_TABLE
WHERE CREATE_DATE &amp;lt; SYSDATE - 보존기간;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1395&quot; data-start=&quot;1349&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발 환경에서 테스트했을 때는 시간이 조금 걸리긴 했지만 정상적으로 삭제가 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1426&quot; data-start=&quot;1397&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 그대로 운영 환경에서도 삭제 작업을 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1461&quot; data-start=&quot;1428&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그런데 삭제 작업을 진행하던 중 인프라 팀에서&amp;nbsp; &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;지금 삭제되는 데이터 때문에 Archive Log가 계속 쌓여서 &lt;/span&gt;DB 남은 용량이 급격히 줄어든 다는 연&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;락이 왔다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1562&quot; data-start=&quot;1540&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1592&quot; data-start=&quot;1569&quot; data-section-id=&quot;1uiuac3&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DELETE는 생각보다 무거운 작업이다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1702&quot; data-start=&quot;1667&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DELETE는 Redo 로깅 최소화가&amp;nbsp; 불가능하다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1721&quot; data-start=&quot;1704&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대량 데이터를 한 번에 삭제하면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;아래와 같은 일이 발생한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1759&quot; data-start=&quot;1723&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1732&quot; data-start=&quot;1723&quot; data-section-id=&quot;17vig44&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Undo 생성&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1742&quot; data-start=&quot;1733&quot; data-section-id=&quot;k7k2ns&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Redo 생성&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1759&quot; data-start=&quot;1743&quot; data-section-id=&quot;g1f5df&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Archive Log 증가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1787&quot; data-start=&quot;1774&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결국 내가 실행한 쿼리는 &lt;b&gt;대량 Archive Log 생성기&lt;/b&gt;와 다름없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1787&quot; data-start=&quot;1774&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1837&quot; data-start=&quot;1830&quot; data-section-id=&quot;njn1jy&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결 방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1862&quot; data-start=&quot;1839&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 삭제 방식을 다음과 같이 변경했다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1957&quot; data-start=&quot;1864&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1906&quot; data-start=&quot;1864&quot; data-section-id=&quot;133qhhj&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cursor + Fetch로 &lt;b&gt;테이블을 한 번만 Full Scan&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1935&quot; data-start=&quot;1907&quot; data-section-id=&quot;193il9t&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;삭제는 &lt;b&gt;작은 단위(batch)&lt;/b&gt; 로 수행&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1957&quot; data-start=&quot;1936&quot; data-section-id=&quot;1wai7kw&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일정 건수마다 &lt;b&gt;Commit&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;1977&quot; data-start=&quot;1959&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예시 구조는 대략 이런 형태였다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773228798209&quot; class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;CURSOR c_log IS
SELECT ROWID INTO ROWIDS
FROM LOG_TABLE
WHERE CREATE_DATE &amp;lt; SYSDATE - 보존기간;

FETCH ...

DELETE FROM LOG_TABLE
WHERE ROWID IN ROWIDS;

COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2153&quot; data-start=&quot;2136&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 방식의 장점은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2231&quot; data-start=&quot;2155&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2183&quot; data-start=&quot;2155&quot; data-section-id=&quot;1jx248x&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테이블 &lt;b&gt;Full Scan은 한 번만 수행&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2207&quot; data-start=&quot;2184&quot; data-section-id=&quot;1sdqepw&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DELETE는 &lt;b&gt;소량 단위로 처리&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2231&quot; data-start=&quot;2208&quot; data-section-id=&quot;qe39e3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Undo / Redo 크기 감소&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2258&quot; data-start=&quot;2233&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 방법으로도 절대적인 삭제하는 용량 자체는 줄일 수 없기 때문에 한번 삭제 작업하고 쌓이는 Archive Log의 유지 기간인 7일 ~ 10일 이후에 지우는 방향으로 변경되어 두달이상은 간간히 삭제했던 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2258&quot; data-start=&quot;2233&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;2297&quot; data-start=&quot;2265&quot; data-section-id=&quot;56baqc&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Enterprise Edition이었다면..?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;2353&quot; data-start=&quot;2299&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 DB가 &lt;b&gt;Oracle Enterprise Edition이고 &lt;/b&gt;날짜 기준으로 Range 파티션이 나뉘어 있다면&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773228812730&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ALTER TABLE LOG_TABLE DROP PARTITION P_2021;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2488&quot; data-start=&quot;2461&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;같은 방식으로 &lt;b&gt;거의 즉시 삭제&lt;/b&gt;가 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2501&quot; data-start=&quot;2490&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장점은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2541&quot; data-start=&quot;2503&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2510&quot; data-start=&quot;2503&quot; data-section-id=&quot;1xrs05d&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빠른 삭제&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2530&quot; data-start=&quot;2511&quot; data-section-id=&quot;7hl74z&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Undo / Redo 부담 감소&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2541&quot; data-start=&quot;2531&quot; data-section-id=&quot;342mzg&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조회 성능 개선&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2612&quot; data-start=&quot;2543&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 회사에서 사용하는 DB는 &lt;b&gt;Oracle Standard Edition&lt;/b&gt;이었기 때문에 이 방법은 사용할 수 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-end=&quot;2617&quot; data-start=&quot;2614&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2636&quot; data-start=&quot;2619&quot; data-section-id=&quot;1heavmi&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이번 작업을 하면서 느낀 점&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쓸데없는 데이터는 바로바로 지우자&lt;/span&gt;&lt;/h4&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 불필요한 데이터를 주기적으로 삭제해주면 단순히 용량을 줄여서 비용을 아끼는 것뿐만 아니라, 조회 성능에도 직접적인 도움이 된다.쓸데없는 데이터 스캔이 줄어들고, 일정 기간의 데이터만 유지하면 전반적인 성능도 안정적으로 유지할 수 있다 &lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대량 삭제시 DELTE는 하지 말자&lt;/span&gt;&lt;/h4&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이건 SQLP 공부하며 알게된 방법인데 ...&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;48&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대량 데이터를 삭제할 때는 DELETE를 그대로 사용하는 건 비효율적인 경우가 많다.데이터와 인덱스를 하나씩 지우면서 리두(REDO), 언두(UNDO) 로그가 많이 발생하고, 그만큼 성능 부담도 커지기 때문이다.그래서 보통은 유지할 데이터만 임시 테이블로 옮긴 뒤, 기존 테이블을 DROP하고 새로 만드는 방식을 사용한다.이 방식은 로그 사용량을 줄일 수 있을 뿐 아니라, DELETE 이후에도 남는 빈 공간 문제까지 같이 해결할 수 있다.DELETE는 데이터를 지워도 물리적인 공간이 줄어들지 않고, 이후 INSERT 시 재사용되는 구조라 테이블이 비효율적으로 커질 수 있다.반면 테이블을 새로 구성하면 이런 불필요한 공간 자체가 사라져서 더 깔끔하게 운영할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2759&quot; data-start=&quot;2669&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;2759&quot; data-start=&quot;2669&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼에도 delete를 써야만 한다면 ...&lt;/span&gt;&lt;/h4&gt;
&lt;p data-end=&quot;2759&quot; data-start=&quot;2669&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;임시 테이블을 활용해 데이터를 보존하는게 좋은건 알지만 실제 운영 상황에선 해당 방법을 선택하기엔 부담이 있다. 그래서 울며겨자먹기로 DELETE를 해야한다면 한 트랜잭션 당 삭제되는 데이터에 제한을 두면서, 삭제될 데이터가 풀스캔이 되야한다면 풀스캔 한번과 여러번의 FETCH 이후 DELETE로 Redo 용량을 적게 쓰도록 노력하면 될 것 같다. 우리 회사에 전문 DBA가 있는 것이 아니라 해당 방법이 정답이 아닐 수도 있지만 일단 이번 건은 이 방법으로 사용되지 않는 7년 간의 데이터를 잘 마무리하고, 삭제 스케줄러를 동작시킬 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2839&quot; data-start=&quot;2836&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>oracle</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/205</guid>
      <comments>https://anythingis.tistory.com/205#entry205comment</comments>
      <pubDate>Wed, 11 Mar 2026 20:44:38 +0900</pubDate>
    </item>
    <item>
      <title>인프런 추석 완강 챌린지 후기 - 비전공자 혼자서 하루 만에 수익화 앱 출시하기</title>
      <link>https://anythingis.tistory.com/207</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요즘 들어 회사 업무만 하다보니 개발 공부를 너무 놓고 있는게 아닌가 싶었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;입사 전부터 막연히 앱을 만들어보고 싶다는 생각을 갖고 있었지만 실행은 안하고 있었습니다...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그러다 요즘 앱 하나 만들어보자는 마음과 나중에 프론트를 할 때도 도움이 될만한 걸 찾다보니 리액트 네이티브가 눈에 띄었고, 리액트도 잘 몰라서 제로초님의 리액트 웹 게임강의 부터 듣고 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmq5R5/btsQ5Gp5RWy/g3cILz61QlINybC0w5dm60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmq5R5/btsQ5Gp5RWy/g3cILz61QlINybC0w5dm60/img.png&quot; data-alt=&quot;무료 강의 감사합니다....&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmq5R5/btsQ5Gp5RWy/g3cILz61QlINybC0w5dm60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdmq5R5%2FbtsQ5Gp5RWy%2Fg3cILz61QlINybC0w5dm60%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;360&quot; height=&quot;414&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;무료 강의 감사합니다....&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;평소에 자바, 스프링을 다루고 요즘엔 그마저도 쿼리만 다루다보니&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; 앱을 만들기 위해 리액트 네이티브를 공부해야하는데, 이를 위해 리액트를 공부하고.. 또 모르는 자바스크립트는 되돌아가서 공부하기에 너무 멀어보였습니다. 추가적으로 &lt;/span&gt;리액트 네이티브를 배우려는 목적 중 하나였던 코드 푸쉬 기능도 사라지고, 아직도 메이저 버전이 없어 버전이 올라갈 때마다 라이브러리의 충돌을 걱정해야된다는 괴담들은 저의 걱정을 심화시켰습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그러다 요즘 AI 기반으로 설계, 코드 작성 등에 도움을 주는 에이전트를 활용한 바이브 코딩 + 플러터라면 이번 추석에 간단하게 앱을 하나 만들어볼 수 있지 않을까 싶어 적당한 강의를 발견했지만 강의비가 살짝 비싼 편이였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3OfLG/btsQ5bcqQ9u/2V0qBzQhsnJP18iq0gxsNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3OfLG/btsQ5bcqQ9u/2V0qBzQhsnJP18iq0gxsNk/img.png&quot; data-alt=&quot;치킨 6마리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3OfLG/btsQ5bcqQ9u/2V0qBzQhsnJP18iq0gxsNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3OfLG%2FbtsQ5bcqQ9u%2F2V0qBzQhsnJP18iq0gxsNk%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;1606&quot; height=&quot;776&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;776&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;치킨 6마리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그러다 이번 인프런에서 만원내면 40% 할인쿠폰을 주는 완강 챌린지를 통해 해당 강의를 듣게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz9t9N/btsQ7cVAfzT/nNsy1mLXzITq2jlchsWw21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz9t9N/btsQ7cVAfzT/nNsy1mLXzITq2jlchsWw21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz9t9N/btsQ7cVAfzT/nNsy1mLXzITq2jlchsWw21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz9t9N%2FbtsQ7cVAfzT%2FnNsy1mLXzITq2jlchsWw21%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;1910&quot; height=&quot;675&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;675&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;앱에 대한 아이디어만 간략히 입력하면 설계와 구현까지 해주는게 굉장히 신기했습니다. 터미널까지 직접 접근해서 명령어 입력하는게 신기하기도하고, 명령어가 여러개 이상일 때는 이전 명령어가 끝나는 걸 기다리지 않고 입력하는ㄱ ㅔai 같다는 생각도.. 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주요 기능과 기술 스택, 패키지 구조까지 잘 짜줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  핵심 가치&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content--핵심-가치&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#-%ED%95%B5%EC%8B%AC-%EA%B0%80%EC%B9%98&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;이유 기반 구매&lt;/b&gt;: 왜 갖고 싶은지 명확히 하여 충동구매 방지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;만족도 추적&lt;/b&gt;: 구매 후 평가로 소비 패턴 개선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;데이터 인사이트&lt;/b&gt;: 시각화된 소비 분석으로 현명한 의사결정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✨ 주요 기능&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content--주요-기능&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#-%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 위시리스트 관리&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content-1-위시리스트-관리&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#1-%EC%9C%84%EC%8B%9C%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EA%B4%80%EB%A6%AC&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;물품명, 예상 가격, 사고 싶은 이유 기록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;드래그앤드롭으로 우선순위 조정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;갤러리/카메라로 이미지 추가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스와이프로 편집/삭제/구매완료&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 구매 목록 관리&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content-2-구매-목록-관리&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#2-%EA%B5%AC%EB%A7%A4-%EB%AA%A9%EB%A1%9D-%EA%B4%80%EB%A6%AC&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위시리스트에서 자동 전환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예상 가격 vs 실제 가격 비교&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;별점으로 만족도 평가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구매 후기 작성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 인사이트 분석&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content-3-인사이트-분석&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#3-%EC%9D%B8%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%B6%84%EC%84%9D&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;월별 지출 그래프&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카테고리별 소비 비율&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;베스트/워스트 구매 리스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기간별 필터링 (올해/6개월/3개월/전체)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; ️ 기술 스택&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content-️-기술-스택&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#%EF%B8%8F-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #f6f8fa; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;background-color: #f6f8fa; color: #1f2328;&quot;&gt;&lt;code&gt;Framework: Flutter 3.x
Language: Dart
State Management: Provider
Database: Hive (Local NoSQL)
Architecture: Clean Architecture&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주요 패키지&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content-주요-패키지&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#%EC%A3%BC%EC%9A%94-%ED%8C%A8%ED%82%A4%EC%A7%80&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;hive&amp;nbsp;- 로컬 데이터베이스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;freezed&amp;nbsp;- 불변 데이터 모델&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;provider&amp;nbsp;- 상태 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;fl_chart&amp;nbsp;- 데이터 시각화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;image_picker&amp;nbsp;- 이미지 선택&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;flutter_slidable&amp;nbsp;- 스와이프 액션&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  프로젝트 구조&lt;/span&gt;&lt;/h2&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a id=&quot;user-content--프로젝트-구조&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/JaegeonYu/Wantly#-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #f6f8fa; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;crystal&quot; style=&quot;background-color: #f6f8fa; color: #1f2328;&quot;&gt;&lt;code&gt;lib/
├── core/           # 공통 요소 (테마, 상수, 유틸)
├── data/           # 데이터 계층 (모델, Repository)
├── domain/         # 도메인 계층 (인터페이스)
└── presentation/   # UI 계층 (화면, Provider)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1501&quot; data-origin-height=&quot;936&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuWtKz/btsQ5zc2mvz/DtKTgA9KvRKZPQ7xQXraQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuWtKz/btsQ5zc2mvz/DtKTgA9KvRKZPQ7xQXraQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuWtKz/btsQ5zc2mvz/DtKTgA9KvRKZPQ7xQXraQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuWtKz%2FbtsQ5zc2mvz%2FDtKTgA9KvRKZPQ7xQXraQ0%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;1501&quot; height=&quot;936&quot; data-origin-width=&quot;1501&quot; data-origin-height=&quot;936&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스크린샷&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM5hyQ/btsQ4NXbWnv/ZX9O77hS7n9n04kShKhXf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM5hyQ/btsQ4NXbWnv/ZX9O77hS7n9n04kShKhXf1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;wishlist.png&quot; width=&quot;216&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM5hyQ/btsQ4NXbWnv/ZX9O77hS7n9n04kShKhXf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM5hyQ%2FbtsQ4NXbWnv%2FZX9O77hS7n9n04kShKhXf1%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bec3EL/btsQ6aEkdPL/KhSz4lBxVoBMxzpHNxcJ8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bec3EL/btsQ6aEkdPL/KhSz4lBxVoBMxzpHNxcJ8K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;whishitem.png&quot; width=&quot;363&quot; height=&quot;645&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bec3EL/btsQ6aEkdPL/KhSz4lBxVoBMxzpHNxcJ8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbec3EL%2FbtsQ6aEkdPL%2FKhSz4lBxVoBMxzpHNxcJ8K%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG1vNC/btsQ7uIyu1o/rnl5YAVuuJXg3lMOKU0Kj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG1vNC/btsQ7uIyu1o/rnl5YAVuuJXg3lMOKU0Kj1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;buylist.png&quot; width=&quot;373&quot; height=&quot;663&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG1vNC/btsQ7uIyu1o/rnl5YAVuuJXg3lMOKU0Kj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG1vNC%2FbtsQ7uIyu1o%2Frnl5YAVuuJXg3lMOKU0Kj1%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eG9gaP/btsQ5CASMFb/MAfjTsq6KbAT2AlPjiNDOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eG9gaP/btsQ5CASMFb/MAfjTsq6KbAT2AlPjiNDOK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;inshight.png&quot; width=&quot;370&quot; height=&quot;658&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eG9gaP/btsQ5CASMFb/MAfjTsq6KbAT2AlPjiNDOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeG9gaP%2FbtsQ5CASMFb%2FMAfjTsq6KbAT2AlPjiNDOK%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;강의 후기&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NoJup/btsQ8ExMK5p/9cQDWBv3CdHBdbV7RZESF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NoJup/btsQ8ExMK5p/9cQDWBv3CdHBdbV7RZESF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NoJup/btsQ8ExMK5p/9cQDWBv3CdHBdbV7RZESF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNoJup%2FbtsQ8ExMK5p%2F9cQDWBv3CdHBdbV7RZESF1%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;318&quot; height=&quot;405&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;설계할 때부터 ai agent와 어떤 단계로 구현할지에 대해 논의하고, 단계별로 구현하면서 컨텍스트를 유지하면서 구현하기가 용이했고, 길을 잃지 않고 끝까지 컨텍스트를 유지하는 역량이 생각보다 뛰어났습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;agent 사용할 때 모드(ask, agent) 등에 대해 이해하고 어떻게 ai에게 물어볼 수 있는지를 배울 수 있었고, 배포를 위해서 해야하는 기본적인 과정(계정 생성, 스크린샷, 아이콘, 스플래쉬)에 대해 배울 수 있었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로컬 db를 넘어 서버와 다양한 api를 활용하려면 언어나 프레임워크를 모르고 만드는 것에 한계가 있을 것 같습니다. 실제로 플러터의 라이프사이클을 모르고 기능만 구현하다 보니 뭐가 문제인지 모르고 커서에 의존하게 되는 경향이 있는 것 같습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;완강 챌린지 후기&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인프런 완강 챌린지에 여행과 놀러가느라 인증을 못했지만 간단하게 앱을 만들어보고 플레이스토어에 등록을 위해 비공개 테스트까지 온 것에 만족합니다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 깃허브 링크&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/Wantly&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/JaegeonYu/Wantly&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>인프런</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/207</guid>
      <comments>https://anythingis.tistory.com/207#entry207comment</comments>
      <pubDate>Sun, 12 Oct 2025 16:32:17 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] 다중 서버 - 단일 DB 사용 시 활용 할  수 있는 배타Lock 동시성 제어</title>
      <link>https://anythingis.tistory.com/204</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNzkLg/btsNm2CdOzg/PmqKuaGLZErCHDJbAPSWe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNzkLg/btsNm2CdOzg/PmqKuaGLZErCHDJbAPSWe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNzkLg/btsNm2CdOzg/PmqKuaGLZErCHDJbAPSWe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNzkLg%2FbtsNm2CdOzg%2FPmqKuaGLZErCHDJbAPSWe1%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;591&quot; height=&quot;150&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 관계에서 사용자가 비밀번호를 변경할 때 최근 사용한 N개의 비밀번호와 일치하는지 확인하는 로직은 어떻게 구현할까?&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;pre id=&quot;code_1744721133381&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional method
{

select * from 
	(select * from password_his where user_id = :user_id order by rev desc)
where rownum &amp;lt;= N;

update user set password = :new_password where user_id = :user_id;

insert into password_his(rev, user_id, password) 
	select max(rev)+1,
    	  user_id,
          :new_password
    from password_his where user_id = :user_id
    
}&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;/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;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 두번째부터 들어온 요청이 첫번째 요청이 커밋되기 전이라면 최근 N차수가 아닌 N-M(M == 동시에 들어온 요청의 수)를 비교한다.&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 동일한 REV+PASSWORD를 가진 데이터가 생길 수 있다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. PASSWORD_HIS의 PK를 REV+USER_ID로 지정해 PK 무결성을 활용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개 이상의 요청이 들어왔을 때 SQL Exception 발생시키고&amp;nbsp; 예외 처리&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. SELECT FOR UPDATE 활용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;오라클 DBMS는 조회시 동시성을 높이기 위한 MVCC 모델을 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DML 문과 다르게 SELECT 문은 읽는 데이터가 다른 트랜잭션에서 변경이 진행중이여도 배타락과 상관없이 공유락을 사용하기 떄문에 읽을 수 있고, 무엇보다 트랜잭션이 시작한 시점으로 돌려서 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 읽은 데이터를 가공하고 UPDATE 할때는 트랜잭션이 시작하는 시점의 블록이 아닌 가장 최근의 블록에 저장하기 때문에&amp;nbsp;해당 작업을 수행하는 트랜잭션은 역설적이게 MVCC 모델이 데이터의 정합성을 떨어트린다.&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;예를 들어 최근 사용한 3개의 비밀번호를 비교하고 사용했던 비밀번호를 사용하지 못한다고 가정해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5번 비밀번호를 변경한 유저가 의도치않게 동시에 3번의 비밀번호 요청을 수행하면 첫번째 요청은 (3,4,5)번째 비밀번호를 비교하고 6번째 비밀번호가 추가되지만, 그 뒤의 두개의 요청은 해당 트랜잭션이 커밋되기 전에 시작하면 (4,5,6), (5,6,7)번째 비밀번호를 비교하는게 아니라 모든 요청이 (3,4,5)번째 비밀번호를 비교하고 변경할 수도 있고 세 개의 요청이 모두 6차수를 가지고 commit 될 수도 있다.&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;서버가 하나라면 synchronized 키워드를 활용할 수 있지만, 서버가 이중화되어있고 동일한 DB를 활용하기에 SELECT에 배타락을 활용하는 SELECT FOR UPDATE를 활용했다. 해당 쿼리는 WHERE 조건을 활용하면 특정 로우에 락을 걸기 때문에 소모되는 비용도 줄일 수 있다.&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;특정 유저의 로우에 락을 걸고 시작한다면 해당 트랜잭션의 시작에 락을 걸기 때문에 멀티쓰레드에 물린 DB커넥션들을&amp;nbsp; 직렬화 시킬 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1744722567910&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional method
{
select * from user where user_id = :user_id for update

/* select * from 
	(select * from password_his where user_id = :user_id order by rev desc)
where rownum &amp;lt;= N;

update user set password = :new_password where user_id = :user_id;

insert into password_his(rev, user_id, password) 
	select max(rev)+1,
    	  user_id,
          :new_password
    from password_his where user_id = :user_id
  */  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Database</category>
      <category>oracle</category>
      <category>select for update</category>
      <category>동시성</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/204</guid>
      <comments>https://anythingis.tistory.com/204#entry204comment</comments>
      <pubDate>Tue, 15 Apr 2025 22:10:22 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] 친절한 SQL 튜닝 6장 1절 기본 DML 튜닝</title>
      <link>https://anythingis.tistory.com/203</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스와 DML 성능&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;INSERT, DELETE&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 인덱스는 정렬된 자료구조이므로 자리를 찾아가서 작업해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;UPDATE&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 변경된 컬럼을 참조하는 인덱스만 찾아서 변경하면 된다(인덱스에 포함된 정보가 변경됐을 때만)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 이 경우는 정렬을 맞춰줘야하기 때문에 이전 &lt;i&gt;&lt;b&gt;인덱스 정보 삭제 후 삽입하는 두 개의 오퍼레이션&lt;/b&gt;&lt;/i&gt; 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;무결성제약과 DML 성능&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;PK, FK 제약은 Check, Not Null 과 다르게 실제 데이터를 조회해봐야하기 때문에 성능에 더 큰 영향을 미친다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Redo 로깅&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Redo 로그는 트랜잭션 데이터가 유실됐을 때 트랜잭션을 재현함으로써 유실 이전 상태로 복구하는데 사용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Redo 로그의 용도는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Database Recovery&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;물리적으로 디스크가 깨지는 Media Fail 발생시 데이터베이스 복구를 위해 사용&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cache Recovery&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;버퍼캐시에 저장된 변경사항이 디스크 상의 데이터 블록에 아직 기록되지 않은 상태에서 인스턴스가 비정상적으로 종료될 대 트랜잭션 데이터 유실에 대비하기 위해 사용&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Fast Commit&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변경된 메모리 버퍼블록을 디스크 상의 데이터 블록에 반영하는 작업은 랜덤 액세스 방식으로 이루어지므로 매우 느림.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반면 로그는 Append 방식으로 기록하므로 상대적으로 빠르다. 버퍼캐시 블록에만 기록된채 아직 디스크에 기록되지 않았지만 Redo 로그를 믿고 빠르게 커밋을 완료한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Undo 로깅&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Undo는 트랜잭션을 롤백함으로써 현재를 과거 상태로 되돌리는데 사용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Undo의 용도는 다음과 같다&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Transatcion Rollback&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션에 의한 변경사항을 최종 커밋하지 않고 롤백하고자 할 때 Undo 데이터 사용&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Transaction Recovery&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템이 셧다운된 시점에 아직 커밋되지 않았던 트랜잭션들을 모두 롤백해야할 때 Undo 데이터 사용&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Read Consistency&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;읽기 일관성을 위해 제공, MVCC 모델에선 select 시 블록 SCN을 쿼리 SCN보다 크면 블록SCN을 Undo 데이터를 적용해 쿼리가 시작된 시점으로 돌려서 읽음 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Commit 과 DML 성능&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션이 데이터를 변경하고 커밋하는 과정, 변경된 블록을 데이터 파일에 기록하는 과정은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DML 문을 실행하면 Redo 로그버퍼에 변경사항을 기록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;버퍼블록에서 데이터를 변경, 버퍼캐시에서 블록을 찾지 못하면 데이터파일에서 읽는 작업부터 진행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커밋&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;LGWR 프로세스가 Redo 로그버퍼 내용을 로그파일에 일괄저장&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DBWR 프로세스가 변경된 버퍼 블록들은 데이터파일에 일괄저장&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;오라클은 데이터 변경 전에 항상 로그부터 기록(1-2, 4-5)&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Q. 버퍼캐시가 휘발성이라 Redo 로그에 기록하는데 Redo 버퍼도 휘발성인데 커밋한 트랜잭션의 영속성을 어떻게 보장?&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;LGWR 프로세스는 &lt;i&gt;&lt;b&gt;'적어도 커밋시점에는'&lt;/b&gt;&lt;/i&gt; Redo 로그버퍼 내용을 로그파일에 기록한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 작업은 디스크 I/O 작업이고 작업이 완료되야 서버프로스세스가 다음 작업을 진행(Sync 작업)할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터베이스 Call&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Parse Call&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL 파싱과 최적화를 수행하는 단계, SQL과 실행 계획을 라이브러리 캐시에서 찾으면(소프트 파싱) 최적화 단계는 생략&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Execute Call&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL 실행하는 단계, DML은 이 단계에서 끝나지만 select는 Fetch 단계를 거친다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Fetch Call&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터를 읽어 사용자에게 결과집합을 전송하는 과정. 전송할 데이터가 많을 때는 Fetch Call이 여러번 발생&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZSZkU/btsM2LVfrmf/l342nqU9aATfxUX5VgBfD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZSZkU/btsM2LVfrmf/l342nqU9aATfxUX5VgBfD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZSZkU/btsM2LVfrmf/l342nqU9aATfxUX5VgBfD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZSZkU%2FbtsM2LVfrmf%2Fl342nqU9aATfxUX5VgBfD1%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;753&quot; height=&quot;287&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;287&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;User Call&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;네트워크를 경유해 DBMS 외부로부터 인입되는 Call&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Recursive Call&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DBMS 내부에서 발생하는 Call. SQl 파싱과 최적화 과정에서 발생하는 데이터 딕셔너리 조회, Pl.SQL로 작성한 사용자 정의함수/프로시저/트리거에 내장된 SQL 실행할 때 발생하는 Call&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 Call(User, Recursive)이든 SQL을 실행할 때 마다 Parse, Execute, Fetch Call 단계를 거친다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Call은 최대한 줄일 것&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;절차적 업무를 위해 PL/SQL를 사용하거나 서버단에서 로직을 분리하고 여러번 쿼리 Call(JPA N+1 문제와 같은)을 하는 것은 성능에 좋지 않다. 가능하면 One SQL 처리할 것&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Array Processing 활용&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼에도 복잡한 업무 로직을 포함하기에 PL/SQL, 서버단에서 로직을 분리해 처리할 때는 Arry Processing 기능을 활용해 Call을 줄일 것&lt;/span&gt;&lt;/p&gt;</description>
      <category>Computer Science/Database</category>
      <category>oracle</category>
      <category>친절한SQL튜닝</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/203</guid>
      <comments>https://anythingis.tistory.com/203#entry203comment</comments>
      <pubDate>Sun, 30 Mar 2025 22:38:36 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] 오라클 성능 고도화의 원리 2권  1장 3절 인덱스 원리와 활용</title>
      <link>https://anythingis.tistory.com/202</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Range Scan&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OEo0N/btsMWV4Rzp1/Da7DkaCeeD81su0xTj0bP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OEo0N/btsMWV4Rzp1/Da7DkaCeeD81su0xTj0bP1/img.png&quot; data-alt=&quot;Index Range Scan&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OEo0N/btsMWV4Rzp1/Da7DkaCeeD81su0xTj0bP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOEo0N%2FbtsMWV4Rzp1%2FDa7DkaCeeD81su0xTj0bP1%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;519&quot; height=&quot;392&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Index Range Scan&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스 루트 블록에서 리프블록까지 수직적으로 탐색한 후 리프 블록을 필요한 범위만 스캔하는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스의 선두컬럼이 조건절에 사용되면 Index Range Scan 활용&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PwpVn/btsMWTlJ2C3/RY2oiIxLKKJjvYNPQexeYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PwpVn/btsMWTlJ2C3/RY2oiIxLKKJjvYNPQexeYk/img.png&quot; data-alt=&quot;Range Scan의 핵심&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PwpVn/btsMWTlJ2C3/RY2oiIxLKKJjvYNPQexeYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPwpVn%2FbtsMWTlJ2C3%2FRY2oiIxLKKJjvYNPQexeYk%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;644&quot; height=&quot;519&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Range Scan의 핵심&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스를 타는 것 자체보다 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;&lt;b&gt;인덱스 스캔 범위를 최소화&lt;/b&gt;&lt;/i&gt;하고 &lt;b&gt;&lt;i&gt;인덱스에서 최대한 필터링하고 테이블 액세스&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;를 하는 것이 중요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Full Scan&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LKtUc/btsMVDKSOeQ/BKbpvLT1IKnAncCoXYFhoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LKtUc/btsMVDKSOeQ/BKbpvLT1IKnAncCoXYFhoK/img.png&quot; data-alt=&quot;Index Full Scan&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LKtUc/btsMVDKSOeQ/BKbpvLT1IKnAncCoXYFhoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLKtUc%2FbtsMVDKSOeQ%2FBKbpvLT1IKnAncCoXYFhoK%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;522&quot; height=&quot;382&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Index Full Scan&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;최초의 수직적 탐색 이후 모든 리프블록을 수평적으로 탐색하는 스캔&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스의 선두컬럼이 조건절에 없으면 우선적으로 옵티마이저는 Table Full Scan을 고려하지만 대용량 테이블이어서 부담이크다면 인덱스를 활용하는 방법을 고려한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터 저장공간은 &lt;b&gt;컬럼 길이&lt;/b&gt; * 레코드 수에 의해 결정되므로 인덱스가 차지하는 면적은 테이블보다 훨씬 적다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzKPqY/btsMXvEx6u2/9hnONZjacoM65AUs51i6pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzKPqY/btsMXvEx6u2/9hnONZjacoM65AUs51i6pk/img.png&quot; data-alt=&quot;Index Full Scan의 효용성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzKPqY/btsMXvEx6u2/9hnONZjacoM65AUs51i6pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzKPqY%2FbtsMXvEx6u2%2F9hnONZjacoM65AUs51i6pk%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;603&quot; height=&quot;493&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Index Full Scan의 효용성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테이블 전체를 스캔하기보다 &lt;span style=&quot;background-color: #f3c000; color: #ffffff;&quot;&gt;&lt;b&gt;인덱스 스캔 단계에서 대부분 레코드를 필터링하고 일부에 대해서만 테이블 액세스&lt;/b&gt;&lt;/span&gt;가 발생하도록 할 수 있다면 Table Full Scan보다 Index Full Scan이 유리하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또 결과 집합이 인덱스 컬럼 순으로 정렬되므로 &lt;b&gt;&lt;span style=&quot;background-color: #f3c000; color: #ffffff;&quot;&gt;Sort Oder by 연산을 생략할 목적&lt;/span&gt;&lt;/b&gt;으로 사용될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6ZKmI/btsMVD48paW/EY0mPAPGE6u42GrkW6ojHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6ZKmI/btsMVD48paW/EY0mPAPGE6u42GrkW6ojHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6ZKmI/btsMVD48paW/EY0mPAPGE6u42GrkW6ojHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6ZKmI%2FbtsMVD48paW%2FEY0mPAPGE6u42GrkW6ojHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;500&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같이 Index에서 필터링 되지 않고 모든 데이터를 다 읽어온다면 &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;모든 테이블의 데이터를 랜덤 액세스로 테이블에 접근하기 때문에 Table Full Scan보다 느릴 가능성이 크다.&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;모든 테이블의 데이터에 접근할 때(일반적으로 Table Full Scan이 Index Full Scan보다 유리) &lt;/span&gt;&lt;b&gt;&lt;i&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;전체 집합 중 처음 일부만을 빠르게 리턴할 목적&lt;/span&gt;으로 first_rows 힌트&lt;/i&gt;&lt;/b&gt;를 활용한다면 옵티마이저는 Table Full Scan이 아닌 Index Full Scan을 활용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때 결과적으로&lt;span style=&quot;color: #000000;&quot;&gt; &lt;i&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;처음 일부가 아닌 끝까지 fetch 한다면 Table Full Scan보다 느리므로 first_rows로 인한 Index Full Scan 활용에 주의&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;해야한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Unique Scan&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baJaG5/btsMVdx31W7/YdJeAt9psfg1HkHvshxLUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baJaG5/btsMVdx31W7/YdJeAt9psfg1HkHvshxLUk/img.png&quot; data-alt=&quot;Index Unique Scan&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baJaG5/btsMVdx31W7/YdJeAt9psfg1HkHvshxLUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaJaG5%2FbtsMVdx31W7%2FYdJeAt9psfg1HkHvshxLUk%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;491&quot; height=&quot;386&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Index Unique Scan&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;수직적 탐색만으로 데이터를 찾는 스캔 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Unique 인덱스를 '=' 조건으로 탐색하는 경우 작동&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Unique 인덱스를 &lt;i&gt;&lt;b&gt;범위검색 조건&lt;/b&gt;&lt;/i&gt;으로 검색하거나, &lt;i&gt;&lt;b&gt;Unique 결합 인덱스에 대해 일부 컬럼으로만 검색&lt;/b&gt;&lt;/i&gt;할 때 Index Range Scan으로 탐색한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Skip Scan&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스 선두 컬럼이 조건절로 사용되지 않으면 옵티마이저는 기본적으로 Table Full Scan 선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Table Full Scan보다&lt;i&gt;&lt;b&gt; I/O를 줄일 수 있거나&lt;/b&gt;&lt;/i&gt;(인덱스 필터링 多 테이블 액세스 小) &lt;i&gt;&lt;b&gt;정렬된 결과를 쉽게 얻을 수 있다&lt;/b&gt;&lt;/i&gt;면 Index Full Scan 활용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Skip Scan은 &lt;b&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;인덱스의 선두 컬럼이 조건절에 빠졌어도 인덱스를 활용하는 방법&lt;/span&gt;&lt;/b&gt;인데, 조&lt;i&gt;&lt;b&gt;건절에 빠진 인덱스 선두 컬럼의 Distinct Value 개수가 적고(ex 성별) 후행 컬럼의 Distinct Value가 많을 때(ex 사원번호)&lt;/b&gt;&lt;/i&gt;&amp;nbsp;유용하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742939456941&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Distinct Value가 적은 것은 인덱스로서 분별력이 낮음, 높은 것은 인덱스로서 분별력이 높음
[사원 테이블에서 남자인 사원 찾기 vs 사원 번호가 1111인 사원 찾기]&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;버퍼 Pinning을 이용한 Skip Scan&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5jcbw/btsMVTfEAQf/FLM7p4OLwQ5Dq5r44Ts8h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5jcbw/btsMVTfEAQf/FLM7p4OLwQ5Dq5r44Ts8h1/img.png&quot; data-alt=&quot;Index Skip Scan&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5jcbw/btsMVTfEAQf/FLM7p4OLwQ5Dq5r44Ts8h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5jcbw%2FbtsMVTfEAQf%2FFLM7p4OLwQ5Dq5r44Ts8h1%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;471&quot; height=&quot;377&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Index Skip Scan&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;리프 블록은 상위 브랜치나 루트 블록을 가리키는 주소 정보를 갖고 있지 않지만 버퍼 Pinning 기법을 활용해 접근한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브랜치 블록 버퍼를 Pinning 한채로 리프 블록에 방문 후 Pinning 한 브랜치 블록으로 되돌아와 다음 방문할 리프 블록을 찾는 과정을 반복한다. 이때 재방문하더라도 Pinning 한 상태이기 때문에 추가적인 블록 I/O는 발생하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스의 최선두 컬럼이 누락되었을 뿐 아니라 &lt;b&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;결합인덱스에서 중간 컬럼이 조건절에 누락되었을 경우&lt;/span&gt;&lt;/b&gt;(a+b+c 인덱스에서 b가 누락) 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또 &lt;b&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;선두컬럼이 부등호, between, like 같은 범위검색 조건&lt;/span&gt;&lt;/b&gt;일 때도 사용될 수 있다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;In - List Iterator와 비교&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742940146479&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from emp -- skip scan
where 연봉 betwwen 2000 and 4000

select * from emp -- In-list
where 연봉 betwwen 2000 and 4000
and 성별 in ('남', '여')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같이 성별에 대한 조건을 추가해주면 Index Skip Scan에 의존하지 않고 빠른 결과집합을 얻을 수 있지만 In-List 명시하려면 성별 값이 더이상 늘지 않음이 보장되어야 한다.&amp;nbsp;또 Index Skip Scan과 마찬가지로 조건절에 누락된 인덱스 선두컬럼의 Distinct Value가 적어야하는 것처럼 In-List로 제공하는 값의 종류가 적어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 In-List == Index Skip Scan은 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Fast Full Scan&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Index Fast Full Scan은 인덱스 트리 구조를 무시하고 인덱스 세그먼트 전체를 MultiBlock Read 방식으로 스캔하기 때문에 Index Full Scan보다 빠르다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결과집합 순서 보장 안됨(인덱스 트리 구조 무시)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;병렬 스캔 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인덱스 포함된 컬럼으로만 조회 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약&amp;nbsp;&lt;i&gt;&lt;b&gt;SQL 내에서 필요한 모든 컬럼(select, where 모두)이 인덱스에 모두 포함&lt;/b&gt;&lt;/i&gt;되어 있지 않다면 &lt;i&gt;&lt;b&gt;Fast Full Scan 이후 모든 데이터에 대해 rowid로 테이블 액세스&lt;/b&gt;&lt;/i&gt;가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Database</category>
      <category>db</category>
      <category>오라클</category>
      <category>오라클 성능 고도화의 원리</category>
      <category>인덱스</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/202</guid>
      <comments>https://anythingis.tistory.com/202#entry202comment</comments>
      <pubDate>Wed, 26 Mar 2025 07:13:26 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] Java 개발자가 읽어본 친절한 SQL튜닝(SQL 파싱)</title>
      <link>https://anythingis.tistory.com/201</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL의 실행과정을 아십니까..?&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;89&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UzTKk/btsMI8p0zp0/lSM4k9HQh9w0AgBKoHSUP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UzTKk/btsMI8p0zp0/lSM4k9HQh9w0AgBKoHSUP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UzTKk/btsMI8p0zp0/lSM4k9HQh9w0AgBKoHSUP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUzTKk%2FbtsMI8p0zp0%2FlSM4k9HQh9w0AgBKoHSUP0%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;89&quot; height=&quot;317&quot; data-origin-width=&quot;89&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;SQLD를 공부할 때까진 이것만 생각했었습니다. 하지만 친절한 SQL 책을 읽으면서 DBMS에 쿼리를 날리면 어떤 과정을 통해 결과집합을 얻는지 배울 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;sql 실행 과정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우선 SQL은 절차적인 언어가 아닌 Structured Query Language로 구조적 질의 언어입니다. 원하는 결과집합을 구조적으로 질의하지만 결과집합을 만들어내는 과정은 절차적으로 수행해야하고 아래와 같은 과정을 거칩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqF1LP/btsMI488Fot/f5Y8HRJrc6O5V4XuOEyl41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqF1LP/btsMI488Fot/f5Y8HRJrc6O5V4XuOEyl41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqF1LP/btsMI488Fot/f5Y8HRJrc6O5V4XuOEyl41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqF1LP%2FbtsMI488Fot%2Ff5Y8HRJrc6O5V4XuOEyl41%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;646&quot; height=&quot;71&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;71&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL에 문법적, 의미상 오류가 없는지 체크하는 파싱&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;최적화 하기 쉬운 형태로 쿼리 변환을 하고 다양한 후보군이 될만한 실행계획 생성 및 비용 계산&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;실행엔진이 실행할 수 있는 프로시저로 변환하는 과정&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;프로시저 실행 후 결과반환&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL 파싱&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원래 DBMS는 I/O작업을 많이 사용하지만 1~3번의 과정은 CPU를 많이 소비하는 연산작업입니다.&amp;nbsp;따라서 파싱된 결과를 라이브러리 캐시에 저장하고 동일한 SQL을 실행할 때 파싱한 프로시저, 예상 실행계획 등을 캐싱해서 사용합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL을 실행할 때 라이브러리 캐시를 통해 CPU 연산을 스킵하면 소프트 파싱, 캐시에서 찾지 못해 CPU 연산을 진행하는 것을 하드 파싱이라 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oBwUU/btsMMK3j0tO/rLa3CAudXAbBmzV8gcij71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oBwUU/btsMMK3j0tO/rLa3CAudXAbBmzV8gcij71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oBwUU/btsMMK3j0tO/rLa3CAudXAbBmzV8gcij71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoBwUU%2FbtsMMK3j0tO%2FrLa3CAudXAbBmzV8gcij71%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;1065&quot; height=&quot;357&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시에 저장하고 활용할 때 SQL 자체가 Key, 프로시저, 예상 실행계획 등은 Value로 활용합니다. 동일한 결과집합을 반환하는 SQL이라도 대소문자, 공백이 조금이라도 다르면 하드 파싱이 진행됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 로그인을 위해 다음과 같은 SQL이 사용자 수만큼 반복해서 하드파싱이 일어나면 해당 서버는 CPU 부담이 높아집니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742105304178&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from user where id = '사용자마다 달라짐' and password ='1234'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;바인드 변수와 Mybatis&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서 SQL의 파라미터 값만 변화하고 하드 파싱의 결과물을 재사용할 수 있도록 하기 위해선 바인드 변수를 활용해야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래와 같이 SQL을 활용하고 바인드 변수값만 바꿔주게 된다면 SQL의 재사용성이 높아집니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742105397852&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from user where id = :userId and password = :usrPassword&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 저는 저도 모르는 사이 바인드 변수를 활용하는 개발자였는데요... 그 이유는&amp;nbsp;&lt;b&gt;자바는 PreparedStatement&lt;/b&gt;를 통해서 바인드 변수를 활용하는 SQL을 만들어주고, &lt;b&gt;Mybatis와 같은 프레임워크 내에서 #{parameter}&lt;/b&gt;를 통해 쿼리를 사용하면 PreparedStatement를 활용하기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKQFGI/btsMMmhxFAw/JW5vsQ6oiUhXKesUkYX1fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKQFGI/btsMMmhxFAw/JW5vsQ6oiUhXKesUkYX1fK/img.png&quot; data-alt=&quot;Mybatis Docs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKQFGI/btsMMmhxFAw/JW5vsQ6oiUhXKesUkYX1fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKQFGI%2FbtsMMmhxFAw%2FJW5vsQ6oiUhXKesUkYX1fK%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;1564&quot; height=&quot;565&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mybatis Docs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 &lt;i&gt;&lt;b&gt;바인드 변수를 활용하면 컬럼의 데이터 분포 통계정보를 활용할 수 없기 때문에 올바른 실행계획으로 유도가 안될 수 있습니다. 그럴땐 Literal SQL로 변환되는 ${paramter}를 포함하는 SQL을 활용해야합니다.&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x0EC4/btsMNjdiD0O/KDkJksWIWqbeoolmHRV4Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x0EC4/btsMNjdiD0O/KDkJksWIWqbeoolmHRV4Ik/img.png&quot; data-alt=&quot;Mybatis Docs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x0EC4/btsMNjdiD0O/KDkJksWIWqbeoolmHRV4Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx0EC4%2FbtsMNjdiD0O%2FKDkJksWIWqbeoolmHRV4Ik%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;1552&quot; height=&quot;715&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mybatis Docs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 방식을 활용할 때는 다음과 같은 유의점이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;사용자로부터 받은 값을 이 방법으로 변경하지 않고 구문에 전달하는 건 안전하지 않다. 이건 잠재적으로 SQL 주입 공격에 노출된다. 그러므로 사용자 입력값에 대해서는 이 방법을 사용하면 안된다. 사용자 입력값에 대해서는 언제나 자체적으로 이스케이프 처리하고 체크해야 한다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;mybatis의 동적 쿼리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 mybatis의 xml 내에서 수많은 if문을 통해 동일한 SQL이여도 실제 실행되는 SQL 자체가 달라지는 경우가 많은데요. SQL의 사용빈도가 높고 동적으로 변환되는 경우도 많다면 DBMS에서 사용하는 CPU 점유율이 높아지게 됩니다. 하지만 지금 하드웨어의 성능 덕분인지 별다른 문제를 만나본적은 없는데요.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 위와 SQL 수행빈도가 높고 동적변환이 잦아 하드 파싱으로 인해 장애가 생긴다면 &lt;i&gt;&lt;b&gt;적절한 SQL의 분리&lt;/b&gt;&lt;/i&gt;를 통해 소프트 파싱이 유도되게끔 하는 방법을 쓰면 되지 않을까 싶습니다. 뇌피셜입니다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마무리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지금까지 SQL은 결과집합이 잘 나오는 것에만 집중했는데 어떤 과정을 통해 결과를 얻게 되는지 아는 것도 중요하다는걸 느꼈습니다. Mybatis에 파라미터 맵핑할 때 #{}, ${}가 있고 왜 #{}를 주로 쓰는건지 알고 쓰면 좋을 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;친절한 SQL 튜닝&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a title=&quot;Mybatis Docs&quot; href=&quot;https://mybatis.org/mybatis-3/ko/sqlmap-xml.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mybatis Docs&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Database</category>
      <category>java</category>
      <category>mybatis</category>
      <category>oracle</category>
      <category>친절한SQL튜닝</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/201</guid>
      <comments>https://anythingis.tistory.com/201#entry201comment</comments>
      <pubDate>Sun, 16 Mar 2025 15:50:09 +0900</pubDate>
    </item>
    <item>
      <title>올해 취업한 개발자의 2024년 회고</title>
      <link>https://anythingis.tistory.com/200</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/74&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/74&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734520873901&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;2022년 25살 회고&quot; data-og-description=&quot;대학교도 졸업하기도 하고 최근에 블로그를 하기 시작하면서 22년도 끝나가는데 올해를 회고해보려고 한다. 개발  Node.js CRUD 게시판 전공자이긴 하지만 학점을 위한 공부만 하고 수업실습도 &quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/74&quot; data-og-url=&quot;https://anythingis.tistory.com/74&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bRfSEh/hyXOcnsQ3z/UGyzJSf9gRU3ydtbHtQsVk/img.jpg?width=300&amp;amp;height=168&amp;amp;face=118_25_164_75,https://scrap.kakaocdn.net/dn/vzlA0/hyXOjmBp69/YW0qfmd19x88JIfCqxfUF1/img.jpg?width=300&amp;amp;height=168&amp;amp;face=118_25_164_75,https://scrap.kakaocdn.net/dn/fp5U7/hyXObWn7Tx/NSSnTc0M7whyx5lJLTNJEk/img.png?width=984&amp;amp;height=918&amp;amp;face=0_0_984_918&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/74&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/74&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bRfSEh/hyXOcnsQ3z/UGyzJSf9gRU3ydtbHtQsVk/img.jpg?width=300&amp;amp;height=168&amp;amp;face=118_25_164_75,https://scrap.kakaocdn.net/dn/vzlA0/hyXOjmBp69/YW0qfmd19x88JIfCqxfUF1/img.jpg?width=300&amp;amp;height=168&amp;amp;face=118_25_164_75,https://scrap.kakaocdn.net/dn/fp5U7/hyXObWn7Tx/NSSnTc0M7whyx5lJLTNJEk/img.png?width=984&amp;amp;height=918&amp;amp;face=0_0_984_918');&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;2022년 25살 회고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;대학교도 졸업하기도 하고 최근에 블로그를 하기 시작하면서 22년도 끝나가는데 올해를 회고해보려고 한다. 개발  Node.js CRUD 게시판 전공자이긴 하지만 학점을 위한 공부만 하고 수업실습도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;22년 회고 이후 오랜만에 써봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바와 스프링을 이제 막 접했던 2년전과 지금 큰 차이는 없는 것 같지만 ... 나중에 보고 얼마나 성장했는가를 위해 작성해봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;싸피 수료&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1학기의 주요 프로젝트인 2인 1조 프로젝트에서 전 백엔드 개발자를 지망했는데 운좋게 프론트엔드 지망하시는 분과 조가 되었습니다. 이전까진 백엔드를 혼자 맡게되면 ERD 설계는 어떻게 하지 하는 고민이 있었는데 기본 게시판 ERD에서 살을 붙여 나가며 지역, 관광지, 유저와 리뷰같은 기능들을 추가했고 기술적으로 더 넣어보고 싶어 백기선님의 스프링부트 프로젝트에서 배운 알림기능을 SSE를 활용해 실시간으로 받아볼 수 있게까지 해볼 수 있었습니다. 또, &lt;b&gt;JPA만 활용하다가 Mybatis&lt;/b&gt;를 활용해보니 &lt;b&gt;특정 지역을 팔로우한 사람들에게 알림을 추가&lt;/b&gt;하거나 &lt;b&gt;특정 유저의&lt;/b&gt; &lt;b&gt;읽지 않은 알림들을 전체 읽음처리&lt;/b&gt;하는 등의 상대적으로 복잡한 SQL문을 구현하기 오히려 쉬운걸 느꼈습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/JaegeonYu/yesterday-s-Trip&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734521954770&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/yesterday-s-Trip: 여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스&quot; data-og-description=&quot;여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스. Contribute to JaegeonYu/yesterday-s-Trip development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; data-og-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdk1KU/hyXOi84jps/ETCR8HLr6WesMnU51syAaK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/sNQ0b/hyXOlxUkmt/kCn6JdrnWLE4eaNRklpmlK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdk1KU/hyXOi84jps/ETCR8HLr6WesMnU51syAaK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/sNQ0b/hyXOlxUkmt/kCn6JdrnWLE4eaNRklpmlK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/yesterday-s-Trip: 여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스. Contribute to JaegeonYu/yesterday-s-Trip development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2학기 때는... 스토리 작가를 위한 작품 시각화 서비스, 내가 뽑은 국회의원의 공약 이행현황을 보여주는 서비스, 프로젝트를 진행하며 디자인, 설계와 개발까지의 긴밀한 협업과 기여도 관리 서비스 등을 진행했습니다. 기술적으로 백엔드 개발과 더불어 인프라 관리를 맡으며 프론트엔드 빌드와 백엔드 빌드과정을 이해하고 &lt;b&gt;배포에 필요한 것들만 최적화로 서버로 이동&lt;/b&gt;시켜 배포하고 &lt;b&gt;과정을 자동화&lt;/b&gt; 하는 것, &lt;b&gt;서버로 들어오는 요청을 컨트롤&lt;/b&gt;하는 부분을 맡았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주로 서비스를 위한 기술보다는 서비스를 배포하기 위한 기술들을 익히면서 나름 재미도 있었지만 돌이켜서 생각해보면 1학기만큼 열정적이고 많이 성장했는가엔 의문이 듭니다. 또 모두 흥미로운 주제였지만 그 주제를 위한 무한 브레인스토밍 기간이 괴롭고 아까웠습니다.(전체 기간이 6~7주라면 아이디어만 2주 이상 생각)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;싸피를 갔던 가장 큰 이유인 '프로젝트 협업 경험'을 채울 수 있었지만 이를 위한 6개월간의 준비과정이 꽤나 깁니다. 협업경험을 인터넷상에서 모임을 구하다가 중간고사 준비한다고, 취업했다고 없어지는 팀원들을 겪으면서 싸피에서 팀경험을 쌓고 싶었지만 준비과정이 매우 길기 때문에 빠르게 취업을 원하시는 분은 생각해보셔야 할 듯합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;싸피를 다닐 때는 다시&lt;/span&gt;&lt;b&gt;&amp;nbsp;대학생으로 돌아간 것 같은 느낌(?)&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;과 함께&amp;nbsp;&lt;/span&gt;&lt;b&gt;취업에 대한 생각보다는 눈앞의 개발&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;만을 바라보고 살면서 &lt;b&gt;아쉬웠던 점&lt;/b&gt;은 &lt;/span&gt;&lt;b&gt;어떤 어려운 문제를 만났고 내가 주도적으로 어떻게 해결했는가&lt;/b&gt;에 대해 프로젝트하면서 꼼꼼히 기록을 안한 것입니다. 기록을 했어도 면접에서 요구하는 것은 어떤 문제가 생겨서 어떤 서비스를 활용하는가에서 끝나는게 아니라 내가 주도적으로 해결하는것에 초점이 맞춰있어 준비하기 힘든부분이기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(ex1. s3 활용중인데 클라이언트단에서 받아보는 속도가 느림 -&amp;gt; cdn 기능 까지 제공하는&amp;nbsp; cloudfront를 쓴다 (X), 내가 캐싱을 구현...(O))&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(ex2. 한국어로 번역을 해야하는데 aws 번역은 속도가 느리다 -&amp;gt; naver clover를 쓰면 한국어 최적화(X), 내가 무언가 ..(O))&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제를 의도적으로 만들고 접근하는 건 모두가 비슷한 문제를 카피하기에 좋지 않은 접근법이라 생각했지만 취업(자소서 + 면접)을 위해서라면 일정 부분이 필요할 수 있겠다.. 라는 생각이 들었습니다. 물론 자신만의 문제를 만나고 주도적으로 해결하는게 제일 좋겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MSA 맛보기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 상반기의 쓴맛과 함께..&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HAXZ1/btsLowSFIZu/Y0YN6fywWzEdwxSq26pG70/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HAXZ1/btsLowSFIZu/Y0YN6fywWzEdwxSq26pG70/img.jpg&quot; data-alt=&quot;출처 : 검정 고무신&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HAXZ1/btsLowSFIZu/Y0YN6fywWzEdwxSq26pG70/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHAXZ1%2FbtsLowSFIZu%2FY0YN6fywWzEdwxSq26pG70%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;225&quot; height=&quot;225&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : 검정 고무신&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;싸피를 수료하고 요즘 개발자라면 한번쯤 들어본 MSA.. 도대체 어떤 놈이길래 이리도 핫이슈일까 하며 공부했습니다. 도메인별로 서비스를 분할하고 기술스택도 모두 다르게 쓸 수 있지만 Spring Cloud 기반의 강의를 들으며 경험을 해봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/MSA&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/JaegeonYu/MSA&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734523709435&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/MSA: msa 아키텍처 활용한 E-Commerce 서비스&quot; data-og-description=&quot;msa 아키텍처 활용한 E-Commerce 서비스. Contribute to JaegeonYu/MSA development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/MSA&quot; data-og-url=&quot;https://github.com/JaegeonYu/MSA&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXB4jD/hyXOd00pIP/XOQg9fTP2yFvjylStF1dg0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/OsSlQ/hyXOno0Jm7/k6SujiO9UAPTSkmkEOuKsk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/MSA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/MSA&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXB4jD/hyXOd00pIP/XOQg9fTP2yFvjylStF1dg0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/OsSlQ/hyXOno0Jm7/k6SujiO9UAPTSkmkEOuKsk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/MSA: msa 아키텍처 활용한 E-Commerce 서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;msa 아키텍처 활용한 E-Commerce 서비스. Contribute to JaegeonYu/MSA development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;간단한 서비스를 분할해보면서 궁금한점들을 해소할 수 있었습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Q1.&amp;nbsp;도메인별로(유저, 글, 댓글) &lt;b&gt;서비스를 분할&lt;/b&gt;하면 &lt;b&gt;DB도 분할&lt;/b&gt;될텐데&amp;nbsp; 글-댓글, 유저-글 등의 &lt;b&gt;외래키 관리&lt;/b&gt;는 어떻게 하는가?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB가 분리되어있기 때문에 외래키 제약을 통한 정합성은 포기하거나 정합성이 필요한 단위만큼 도메인을 분할&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Q2. 서비스끼리의 연동은 어떻게&lt;b&gt; ?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;b&gt;동기처리&lt;/b&gt;가 필요할땐 직접 다른 서비스 API 콜을 하거나, 큐잉 시스템에 발행 및 처리될 때까지 펜딩처리&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;b&gt;비동기처리&lt;/b&gt;가 필요할 땐 &lt;/span&gt;&lt;span style=&quot;&quot;&gt;큐잉 시스템을 활용&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;Q3. 이중화된 서버의 DB 부하 및 싱크 처리는?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;싱크 처리&lt;a title=&quot;싱크처리&quot; href=&quot;https://anythingis.tistory.com/193#Kafka%20Connect%20%EC%8B%A4%ED%96%89-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(https://anythingis.tistory.com/193#Kafka%20Connect%20%EC%8B%A4%ED%96%89-1)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735452012634&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;[MSA] Apache Kafka &amp;amp; Kafka Connect&quot; data-og-description=&quot;KafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있을 수 있지만 아래와 같은 단점이 있습니다.&amp;nbsp;데이터 연동의&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/193#Kafka%20Connect%20%EC%8B%A4%ED%96%89-1&quot; data-og-url=&quot;https://anythingis.tistory.com/193&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uza6R/hyXSEkAGLM/2B3lpLjflhGTIMp4KMlDZ0/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/fDuED/hyXSBnSMvv/KpaktDsIwd3T697FakvXkk/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/t2GtE/hyXSsYLQYU/yZ6Bg0OWZcqdruFjk1LRS1/img.png?width=939&amp;amp;height=450&amp;amp;face=647_90_724_365&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/193#Kafka%20Connect%20%EC%8B%A4%ED%96%89-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/193#Kafka%20Connect%20%EC%8B%A4%ED%96%89-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uza6R/hyXSEkAGLM/2B3lpLjflhGTIMp4KMlDZ0/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/fDuED/hyXSBnSMvv/KpaktDsIwd3T697FakvXkk/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/t2GtE/hyXSsYLQYU/yZ6Bg0OWZcqdruFjk1LRS1/img.png?width=939&amp;amp;height=450&amp;amp;face=647_90_724_365');&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;[MSA] Apache Kafka &amp;amp; Kafka Connect&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;KafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있을 수 있지만 아래와 같은 단점이 있습니다.&amp;nbsp;데이터 연동의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 서버의 DB 부하를 줄이기 위한 Kafka로 위임 (&lt;a title=&quot;서버의 DB 부하를 줄이기 위한 Kafka로 위임&quot; href=&quot;https://anythingis.tistory.com/195&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://anythingis.tistory.com/195)&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735452066281&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;[MSA] INSERT Query 스프링에서 Kafka로 위임하기&quot; data-og-description=&quot;개요https://anythingis.tistory.com/193 &amp;nbsp;[MSA] Apache Kafka &amp;amp; Kafka ConnectKafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/195&quot; data-og-url=&quot;https://anythingis.tistory.com/195&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jqb1B/hyXSr6CIxf/ypc1xJfaObYvPFv17wR5MK/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/RVbr4/hyXSyLtIQ8/kl5UbfcGE8q9HOoyD0GLF1/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/dil0AL/hyXSzjg39D/84bpkK53KHTIqiCb7rpek1/img.png?width=1525&amp;amp;height=533&amp;amp;face=0_0_1525_533&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/195&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/195&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jqb1B/hyXSr6CIxf/ypc1xJfaObYvPFv17wR5MK/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/RVbr4/hyXSyLtIQ8/kl5UbfcGE8q9HOoyD0GLF1/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/dil0AL/hyXSzjg39D/84bpkK53KHTIqiCb7rpek1/img.png?width=1525&amp;amp;height=533&amp;amp;face=0_0_1525_533');&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;[MSA] INSERT Query 스프링에서 Kafka로 위임하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요https://anythingis.tistory.com/193 &amp;nbsp;[MSA] Apache Kafka &amp;amp; Kafka ConnectKafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k2mNz/btsLmqz4jYd/Lbe2cdS37P8ykbMleTFxkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k2mNz/btsLmqz4jYd/Lbe2cdS37P8ykbMleTFxkk/img.jpg&quot; data-alt=&quot;출처 : tvn snl&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k2mNz/btsLmqz4jYd/Lbe2cdS37P8ykbMleTFxkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk2mNz%2FbtsLmqz4jYd%2FLbe2cdS37P8ykbMleTFxkk%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;680&quot; height=&quot;379&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : tvn snl&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MSA가 필요할만큼 서버의 부하를 받아본 경험이 있는 건 아니긴 하지만, &lt;b&gt;도메인을 분할하는 설계&lt;/b&gt;와 &lt;b&gt;서버끼리의 통신&lt;/b&gt;(로직에 따라 동기, 비동기 처리)에 대해 강의를 통해 간접적으로 체험해보고 개념적인 부분과 기술적인 부분을 배워볼 수 있었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;취업&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개판 오분전으로 쓴 삼성 SDI 자소서가 통과하고 코테도 붙으면서 인생 첫 면접을 삼성에서 보는 불상사가 발생했습니다. 굉장히 준비를 잘못했구나와 함께 다른 회사면접에도 경험할 수 있을까라는 기대를 했지만 ... 취업시장의 추위는 온몸으로 느낄 수 있었습니다. 그러다 구매 솔루션을 하는 기업인 엠로에 취업하게 됐고 3개월이 지난 현재 정규직 전환을 앞두고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj8MGf/btsLoMBbPWj/x6xMSLMS9oScXgtY0M4oY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj8MGf/btsLoMBbPWj/x6xMSLMS9oScXgtY0M4oY1/img.png&quot; data-alt=&quot;굿&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj8MGf/btsLoMBbPWj/x6xMSLMS9oScXgtY0M4oY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj8MGf%2FbtsLoMBbPWj%2Fx6xMSLMS9oScXgtY0M4oY1%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;735&quot; height=&quot;246&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;굿&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;돌이켜 생각해보면 상반기 시장에선 기업에 대해 알아보지도 않고 우다다 자소서 난사를 하다가 상반기가 잠잠해지고 &lt;b&gt;오히려 시간이 있는 상태&lt;/b&gt;에서 &lt;b&gt;기업을 조사하며 매력을 느끼고 내가 남들과 다르게 기여할 수 있는 점&lt;/b&gt;을 고민해본게 자소서에도 면접에서도 많은 차이가 있던 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w6u4K/btsLAt3IqmF/8tOqgOKLjhAkzAQRLPkrKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w6u4K/btsLAt3IqmF/8tOqgOKLjhAkzAQRLPkrKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w6u4K/btsLAt3IqmF/8tOqgOKLjhAkzAQRLPkrKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw6u4K%2FbtsLAt3IqmF%2F8tOqgOKLjhAkzAQRLPkrKK%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;647&quot; height=&quot;424&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기업을 위한 다양한 시스템 중 전사적 자원관리 시스템인 &lt;b&gt;ERP&lt;/b&gt;는 국제적으로 SAP, 나라별로도 SAP를 제외하고 &lt;b&gt;꽉 잡고있는 기업&lt;/b&gt;(국내는 더존)들이 있고 성&lt;b&gt;장을 시작한지도 오래된 개념의 기술&lt;/b&gt;이기에 &lt;i&gt;실제 업무에 맞게 최적화와 정형화 되어있는 분야&lt;/i&gt;라고 생각합니다.&amp;nbsp; 하지만&amp;nbsp; SRM, CRM은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기업별로 분업의 역할이 더욱 강해지면서 성장하기 시작한게 &lt;b&gt;비교적 최근&lt;/b&gt;이고&lt;b&gt; 감당하는 &lt;/b&gt;&lt;/span&gt;&lt;b&gt;범위도 더 넓어&lt;/b&gt; 제가 최적화를 위해 기여할 수 있는 부분이 많다고 생각하는 부분과 기초적인 개발지식과 클라우드 환경에서 서버 인프라를 구축해본 경험을 어필했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;취업준비하시는 분들 모두 자기와 맞는 회사를 곰곰히 고민해보시면 좋을 결과가 있을거라고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;앞으로의 목표&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요즈음은 웹 서비스의 3 티어 아키텍처 중 DB와 애플리케이션 사이에 대해 관심이 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4wjmx/btsLoE4e3TL/JZhldMGcauftksP2FqBy6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4wjmx/btsLoE4e3TL/JZhldMGcauftksP2FqBy6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4wjmx/btsLoE4e3TL/JZhldMGcauftksP2FqBy6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4wjmx%2FbtsLoE4e3TL%2FJZhldMGcauftksP2FqBy6K%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;740&quot; height=&quot;433&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제 운영하는 서비스를 처음 접해보면서 지금까지 프로젝트에서 Long 타입의 Auto Increment 옵션의 PK만 활용하던 것이 꽤나 괴리가 있다는 걸 느꼈습니다. 친절한 SQL 튜닝 책을 읽으면서 개발자로서 OLTP 환경에서 인덱스의 중요성을 깨닫고 SQL이 인덱스를 타는지 안타는지를 넘어, 인덱스 스캔의 범위나 필터링의 범위, 옵티마이저가 어떤 조인을 고려하는지까지 배울 수 있었습니다. 꾸준히 공부하면서 25년 SQLP를 목표로 할 것 같습니다. 딸 수 있겠죠 ..? &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;앞으로도 성장을 멈추지 않는 사람이 되기를 바라봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;</description>
      <category>회고</category>
      <category>싸피</category>
      <category>엠로</category>
      <category>회고</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/200</guid>
      <comments>https://anythingis.tistory.com/200#entry200comment</comments>
      <pubDate>Wed, 18 Dec 2024 22:01:55 +0900</pubDate>
    </item>
    <item>
      <title>Spring Transaction</title>
      <link>https://anythingis.tistory.com/199</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/171&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/171&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722579171875&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;[DB] 트랜잭션, 애플리케이션의 트랜잭션 제어&quot; data-og-description=&quot;트랜잭션이란 데이터를 저장할 때 단순히 파일에 저장해도 되지만, 데이터베이스에 저장하는 가장 큰 이유입니다. 트랜잭션은 그대로 번역하면 거래인데, 데이터베이스에선 하나의 거래를 안&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/171&quot; data-og-url=&quot;https://anythingis.tistory.com/171&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdwCtW/hyWKDeQmSG/F4H9jiaHgXGcSs86562h50/img.png?width=762&amp;amp;height=449&amp;amp;face=0_0_762_449,https://scrap.kakaocdn.net/dn/AoUUK/hyWG1BG0Im/Uxn3tmhCmeHPTyeh3tji20/img.png?width=762&amp;amp;height=449&amp;amp;face=0_0_762_449,https://scrap.kakaocdn.net/dn/bclILk/hyWKHuMthu/XcK3WzYKkCaAKTinFM6eTK/img.png?width=762&amp;amp;height=449&amp;amp;face=0_0_762_449&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/171&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/171&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdwCtW/hyWKDeQmSG/F4H9jiaHgXGcSs86562h50/img.png?width=762&amp;amp;height=449&amp;amp;face=0_0_762_449,https://scrap.kakaocdn.net/dn/AoUUK/hyWG1BG0Im/Uxn3tmhCmeHPTyeh3tji20/img.png?width=762&amp;amp;height=449&amp;amp;face=0_0_762_449,https://scrap.kakaocdn.net/dn/bclILk/hyWKHuMthu/XcK3WzYKkCaAKTinFM6eTK/img.png?width=762&amp;amp;height=449&amp;amp;face=0_0_762_449');&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;[DB] 트랜잭션, 애플리케이션의 트랜잭션 제어&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;anythingis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주로 DB에서 활용되는 개념이지만, 애플리케이션에서 데이터를 다루고 DB에 저장하기에, DB에서 커밋이 되거나 롤백 된 후 비즈니스 로직까지 연계된 로직을 처리하기 위해 스프링도 트랜잭션 기능을 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 트랜잭션&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링은 다양한 DB 접근 기술과 같이 활용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;JDBC&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722580252638&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void accountTransfer(String fromId, String toId, int money) throwsSQLException {
     Connection con = dataSource.getConnection();
     try {
         con.setAutoCommit(false); //트랜잭션 시작
         //비즈니스 로직
         bizLogic(con, fromId, toId, money);
         con.commit(); //성공시 커밋
     } catch (Exception e) {
         con.rollback(); //실패시 롤백
         throw new IllegalStateException(e);
     } finally {
         release(con);
     }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;JPA&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722580281447&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
     //엔티티 매니저 팩토리 생성
     EntityManagerFactory emf =
    Persistence.createEntityManagerFactory(&quot;jpabook&quot;);
     EntityManager em = emf.createEntityManager(); //엔티티 매니저 생성
     EntityTransaction tx = em.getTransaction(); //트랜잭션 기능 획득
     
     try {
         tx.begin(); //트랜잭션 시작
         logic(em); //비즈니스 로직
         tx.commit();//트랜잭션 커밋
     } catch (Exception e) {
         tx.rollback(); //트랜잭션 롤백
     } finally {
         em.close(); //엔티티 매니저 종료
         }
     emf.close(); //엔티티 매니저 팩토리 종료
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 각가의 데이터 접근 기술들은 트랜잭션을 처리하는 방식에 차이가 있어 위와 같이 활용하면 DB접근 기술의 변화는 트랜잭션을 활용하는 코드를 모두 수정하는 결과를 초래합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; PlatformTransactionManager &lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링은 이런 문제를 트랜잭션 매니저를 추상화해서 해결합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722580486150&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.springframework.transaction;

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 획득, 커밋, 롤백으로 추상화합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qyVnY/btsIS6C9OdG/i1nYki2TBXxKAaDF6Yt4YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qyVnY/btsIS6C9OdG/i1nYki2TBXxKAaDF6Yt4YK/img.png&quot; data-alt=&quot;스프링 트랜잭션 추상화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qyVnY/btsIS6C9OdG/i1nYki2TBXxKAaDF6Yt4YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqyVnY%2FbtsIS6C9OdG%2Fi1nYki2TBXxKAaDF6Yt4YK%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;678&quot; height=&quot;272&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스프링 트랜잭션 추상화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제 트랜잭션을 활용하는 코드에서는 PlatfornTransactionManager를 활용하면 DB 접근 기술이 변화해도 사용하는 DB 접근 기술을 확인하고 그에 맞는 인터페이스 구현체 갈아끼는 방식으로 코드의 변화가 필요 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 트랜잭션 관리&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;프로그래밍 방식 트랜잭션 관리 &lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722580194922&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
         //비즈니스 로직
         bizLogic(fromId, toId, money);
         transactionManager.commit(status); //성공시 커밋
    } catch (Exception e) {
         transactionManager.rollback(status); //실패시 롤백
         throw new IllegalStateException(e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 관리 로직(획득, 커밋, 롤백)과 비즈니스 로직이 같은 코드내에 존재하는 방식이 &amp;nbsp;프로그래밍 방식 트랜잭션 관리입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;코드를 보며 의도 파악은 빠르게 할 수 있지만 관점분리가 안되어 복잡성이 증가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;선언형 트랜잭션 관리&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722579434766&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@Transcational
public class UserService {

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Transactional을 통해 메소드나 클래스가 트랜잭션을 사용하는 것을 나타내는 방식이 선언형 트랜잭션 관리입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;트랜잭션 관리를 AOP 관점분리 추상화를 통해 &lt;i&gt;&lt;b&gt;개발자는 비즈니스 로직에 집중&lt;/b&gt;&lt;/i&gt;할 수 있습니다. &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;i&gt;&lt;b&gt;하지만 추상화된 부분이 어떻게 동작하는지 알아야 예상한대로 동작하지 않는 논리적 오류가 발생했을 때 디버깅을 할 수 있습니다.&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 트랜잭션과 프록시&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;간편하게 비즈니스 로직에 집중할 수 있게 해주는 선언형 트랜잭션 관리를 활용하면서 스프링 AOP를 활용하는 프록시 객체로 &lt;b&gt;한단계의 추상화&lt;/b&gt;합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프록시 도입전&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bow7JI/btsIS8A3uRK/JKbt5BOAvuCzvUNjkpSkeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bow7JI/btsIS8A3uRK/JKbt5BOAvuCzvUNjkpSkeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bow7JI/btsIS8A3uRK/JKbt5BOAvuCzvUNjkpSkeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbow7JI%2FbtsIS8A3uRK%2FJKbt5BOAvuCzvUNjkpSkeK%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;815&quot; height=&quot;418&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; 프록시 도입후&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션과 비즈니스 로직의 관점 분리를 위해 트랜잭션을 관리하는 프록시 객체를 생성하고, 비즈니스 로직은 원래 객체를 참조합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yYaIX/btsIScw85R3/KoT6aAfTK4W2xoSxeS3KS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yYaIX/btsIScw85R3/KoT6aAfTK4W2xoSxeS3KS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yYaIX/btsIScw85R3/KoT6aAfTK4W2xoSxeS3KS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyYaIX%2FbtsIScw85R3%2FKoT6aAfTK4W2xoSxeS3KS1%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;1181&quot; height=&quot;432&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프록시 도입 후 트랜잭션 흐름&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2582&quot; data-origin-height=&quot;1406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOIbkE/btsIRxuV5D4/qYrMbQruOcjQz9iXjp4tTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOIbkE/btsIRxuV5D4/qYrMbQruOcjQz9iXjp4tTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOIbkE/btsIRxuV5D4/qYrMbQruOcjQz9iXjp4tTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOIbkE%2FbtsIRxuV5D4%2FqYrMbQruOcjQz9iXjp4tTK%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;2582&quot; height=&quot;1406&quot; data-origin-width=&quot;2582&quot; data-origin-height=&quot;1406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;트랜잭션 적용 확인&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;선언형 트랜잭션 관리를 활용하는 메소드와 그렇지 않은 메소드 내에서 트랜잭션 동기화 매니저에 접근해 트랜잭션이 활성화중인지 체크할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722577970932&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@SpringBootTest
public class TxBasicTest {

    @Slf4j
    static class BasicService{
        @Transactional
        public void tx(){
            log.info(&quot;call tx&quot;);
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info(&quot;tx active = {}&quot;, txActive);
        }

        public void noneTx(){
            log.info(&quot;call tx&quot;);
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info(&quot;tx active = {}&quot;, txActive);
        }
    }

    @Autowired
    BasicService basicService;

    @TestConfiguration
    static class TxApplyBasicConfig{
        @Bean
        BasicService basicService(){
            return new BasicService();
        }
    }

    @Test
    void txTest(){
        basicService.tx();
        basicService.noneTx();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722577950979&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2024-08-02T14:45:07.981+09:00  INFO 4728 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : call tx
2024-08-02T14:45:07.981+09:00  INFO 4728 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : tx active = true
2024-08-02T14:45:07.985+09:00  INFO 4728 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : call tx
2024-08-02T14:45:07.985+09:00  INFO 4728 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : tx active = false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션을 감지하는 패키지의 로깅 레벨을 TRACE로 낮춰서 로그를 찍어본다면 트랜잭션 획득과 커밋 로그를 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.properties 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722578014977&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;logging.level.org.springframework.transaction.interceptor=TRACE&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722578044864&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2024-08-02T14:53:50.355+09:00 TRACE 11908 --- [SrpingDB2] [    Test worker] o.s.t.i.TransactionInterceptor           : Getting transaction for [com.example.srpingdb2.TxBasicTest$BasicService.tx]
2024-08-02T14:53:50.355+09:00  INFO 11908 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : call tx
2024-08-02T14:53:50.356+09:00  INFO 11908 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : tx active = true
2024-08-02T14:53:50.356+09:00 TRACE 11908 --- [SrpingDB2] [    Test worker] o.s.t.i.TransactionInterceptor           : Completing transaction for [com.example.srpingdb2.TxBasicTest$BasicService.tx]
2024-08-02T14:53:50.358+09:00  INFO 11908 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : call tx
2024-08-02T14:53:50.359+09:00  INFO 11908 --- [SrpingDB2] [    Test worker] c.e.srpingdb2.TxBasicTest$BasicService   : tx active = false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuV8AT/btsIR2nS7F5/8JeuVTklkjarWatWTqE9UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuV8AT/btsIR2nS7F5/8JeuVTklkjarWatWTqE9UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuV8AT/btsIR2nS7F5/8JeuVTklkjarWatWTqE9UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuV8AT%2FbtsIR2nS7F5%2F8JeuVTklkjarWatWTqE9UK%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;784&quot; height=&quot;165&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빈을 생성할 때 &lt;i&gt;&lt;b&gt;클래스, 메소드의 트랜잭션 여부&lt;/b&gt;&lt;/i&gt;를 스캔하고 &lt;i&gt;&lt;b&gt;하나라도 적용이 되어 있다면&lt;/b&gt;&lt;/i&gt; 트랜잭션 프록시 빈 생성합니다. 만약 트랜잭션 적용이 아예 없는 빈은 프록시 객체가 아닌 순수한 객체가 애플리케이션 컨텍스트에 적용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드 호출 시마다 메소드의 트랜잭션 사용여부를 확인하고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 사용한다면 트랜잭션을 실행하고 비즈니스 로직을 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 사용하지 않으면 순수한 비즈니스 로직만 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프록시 객체는 기존 객체를 상속해서 사용하기에 다형성을 통해 의존성 주입을 받아 활용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 AOP 주의사항&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부 호출&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 클래스에 트랜잭션이 붙는 경우가 아니고&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 트랜잭션 적용 안된 메소드에서 트랜잭션 메소드를 호출할 때&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션이 적용되지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KvPj2/btsISOW5vlA/5Y2qfpA0fy1vhrnz9cfUkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KvPj2/btsISOW5vlA/5Y2qfpA0fy1vhrnz9cfUkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KvPj2/btsISOW5vlA/5Y2qfpA0fy1vhrnz9cfUkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKvPj2%2FbtsISOW5vlA%2F5Y2qfpA0fy1vhrnz9cfUkk%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;940&quot; height=&quot;453&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 그 이유는 프록시 객체를 호출하고 트랜잭션이 적용안된 메소드를 실행하면 트랜잭션 없이 실제 객체의 비즈니스 로직을 호출한 뒤, 내부 로직을 호출할 때 별다른 키워드가 없으면 this.method()를 호출하기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722586268328&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@SpringBootTest
public class InternalTest {
    @TestConfiguration
    static class InternalConfig{
        @Bean
        public InternalService internalService(){
            return new InternalService();
        }
    }

    @Slf4j
    static class InternalService{
        public void external(){
            log.info(&quot;call external&quot;);
            printTxInfo();
            internal();
        }

        @Transactional
        public void internal(){
            log.info(&quot;call internal&quot;);
            printTxInfo();
        }

        private void printTxInfo() {
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info(&quot;tx active={}&quot;, txActive);
        }
    }

    @Autowired InternalService internalService;

    @Test
    public void callExternal(){
        internalService.external();
    }

    @Test
    public void callInternal(){
        internalService.internal();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722586299815&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;InternalTest$InternalService       : call external
InternalTest$InternalService       : tx active=false
InternalTest$InternalService       : call internal
InternalTest$InternalService       : tx active=false&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oudG1/btsIUsymaLZ/UHYDtEKLmzbVc5uosat94K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oudG1/btsIUsymaLZ/UHYDtEKLmzbVc5uosat94K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oudG1/btsIUsymaLZ/UHYDtEKLmzbVc5uosat94K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoudG1%2FbtsIUsymaLZ%2FUHYDtEKLmzbVc5uosat94K%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;708&quot; height=&quot;620&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;가장 쉬운 해결방법은 트랜잭션을 타는 메소드를 클래스를 분리해서 다시 프록시 객체를 호출해 트랜잭션 관리 로직을 호출하게 하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Only public 접근제한자 트랜잭션 AOP 적용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;private, package, protected 등의 접근제한자는 트랜잭션 AOP 적용이 제한됩니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;이는 스프링이 막아둔 것으로 의도하지 않는 곳까지 트랜잭션이 과도하게 적용되는 것을 막기위해서입니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;트랜잭션은 주로 비즈니스 로직의 시작점에 걸기 때문에 외부에 열어준 곳을 시작점으로 활용하기 때문에&amp;nbsp; public 메서드에만 트랜잭션을 적용하도록 설정되어 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722587016298&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public class Hello {
 public method1(); // TX 적용
 method2(): // TX X
 protected method3(); // TX X
 private method4(); // TX X
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 예외가 발생하는 것이 아닌 트랜잭션 적용만 무시되므로, 외부 호출 시작점이 public 메소드가 아닐 때 주의해야합니다.&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;초기화 시점에 트랜잭션 활용시&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@PostConstruct와 같이 빈 초기화 시에 실행되는 메소드 이후에 트랜잭션 AOP가 적용되기에 초기화 시점에서 트랜잭션을 획득할 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@EvenListener(ApplicatonReadyEvent.class)을 활용해 애플리케이션이 가동될 때로 메소드 호출을 지연시키면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;정리&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;스프링의 트랜잭션 추상화 - 플랫폼 트랜잭션 매니저&lt;/span&gt;&lt;span style=&quot;&quot;&gt;선언형 트랜잭션 적용 - AOP 활용한 프록시 객체&lt;/span&gt;&lt;span style=&quot;&quot;&gt;주의점 - 동일 클래스 트랜 x 메소드 -&amp;gt; 트랜 O 메소드 호출 시 트랜잭션 적용 X&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722587400865&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;스프링 DB 2편 - 데이터 접근 활용 기술 강의 | 김영한 - 인프런&quot; data-og-description=&quot;김영한 | 백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔드&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/스프링-db-2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b8Y6uw/hyWKIAsKSa/rzhM9SMukU2LO8fnuNqSJ0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/eFjkF/hyWGPVxT3Q/knKSlhL8RSkqVNbr1g5Z2k/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/xXIjp/hyWGTKo33E/qR4zXnNluK1tKmU1GPDAD1/img.png?width=1238&amp;amp;height=775&amp;amp;face=1071_657_1142_734&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b8Y6uw/hyWKIAsKSa/rzhM9SMukU2LO8fnuNqSJ0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/eFjkF/hyWGPVxT3Q/knKSlhL8RSkqVNbr1g5Z2k/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/xXIjp/hyWGTKo33E/qR4zXnNluK1tKmU1GPDAD1/img.png?width=1238&amp;amp;height=775&amp;amp;face=1071_657_1142_734');&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;스프링 DB 2편 - 데이터 접근 활용 기술 강의 | 김영한 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;김영한 | 백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.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;</description>
      <category>개발/스프링</category>
      <category>spring</category>
      <category>transanction</category>
      <category>스프링</category>
      <category>트랜잭션</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/199</guid>
      <comments>https://anythingis.tistory.com/199#entry199comment</comments>
      <pubDate>Fri, 2 Aug 2024 17:30:38 +0900</pubDate>
    </item>
    <item>
      <title>Jenkins Nginx Reverse Proxy 적용</title>
      <link>https://anythingis.tistory.com/198</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AWS 클라우드 서버에서 백엔드와 프론트엔드를 배포중이고, 젠킨스까지 하나의 서버에서 진행하려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx 활용한 리버스 프록시, SSL 적용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a title=&quot;Nginx 활용한 프록시 설정, SSL 설정 포스팅&quot; href=&quot;https://anythingis.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/175&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722404573872&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;프록시 서버, Nginx 설정(리버스 프록시, SSL)(Ubuntu 24.03ver)&quot; data-og-description=&quot;프록시 서버 왜 쓸까?기본 웹 서비스 통신의 방식입니다.www.naver.com을 치면&amp;nbsp;이런 화면을 받아오는 것과 같습니다.&amp;nbsp;&amp;nbsp;이후 로그인을 하는 등 비즈니스 로직과 관련된 부분을 동작하면 백엔드에 &quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/175&quot; data-og-url=&quot;https://anythingis.tistory.com/175&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFpDeR/hyWGYq86mj/txc9M8bntUt5ESKkacklh0/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/y6Ief/hyWGPAVvI8/fVAnO5uaKvHEGBE32mHedk/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/eDcdL/hyWGZp0VHm/IIeLOA8kaWHQhCqbgcJY00/img.png?width=1755&amp;amp;height=545&amp;amp;face=0_0_1755_545&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/175&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFpDeR/hyWGYq86mj/txc9M8bntUt5ESKkacklh0/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/y6Ief/hyWGPAVvI8/fVAnO5uaKvHEGBE32mHedk/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/eDcdL/hyWGZp0VHm/IIeLOA8kaWHQhCqbgcJY00/img.png?width=1755&amp;amp;height=545&amp;amp;face=0_0_1755_545');&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;프록시 서버, Nginx 설정(리버스 프록시, SSL)(Ubuntu 24.03ver)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프록시 서버 왜 쓸까?기본 웹 서비스 통신의 방식입니다.www.naver.com을 치면&amp;nbsp;이런 화면을 받아오는 것과 같습니다.&amp;nbsp;&amp;nbsp;이후 로그인을 하는 등 비즈니스 로직과 관련된 부분을 동작하면 백엔드에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;직접적인 포트노출을 줄이고 HTTPS 적용을 위해 Nginx를 활용해 리버스 프록시 서버로 활용중이고 젠킨스를 추가 설정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;활용중인 Nginx Conf&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722403337819&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    server_name domain;
    listen 443 ssl;

     ssl_certificate /etc/letsencrypt/live/domain/fullchain.pem; 
     ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem; 

    location /api { 
        proxy_pass http://localhost:8080;
        proxy_set_header X-Real-IP $remote_addr;
        error_page 405 = $uri;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

     location / { 
         root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;
#        proxy_pass http://localhost:3000;
	}
}
server { 
    if ($host = domain) { 
        return 301 https://$host$request_uri;
    } # managed by Certbot

  listen 80;
  server_name domain;
    return 404; # managed by Certbot
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbRJIV/btsISGwZT7K/mm9Gn2oaVDoSMLEIGHkyMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbRJIV/btsISGwZT7K/mm9Gn2oaVDoSMLEIGHkyMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbRJIV/btsISGwZT7K/mm9Gn2oaVDoSMLEIGHkyMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbRJIV%2FbtsISGwZT7K%2Fmm9Gn2oaVDoSMLEIGHkyMK%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;1024&quot; height=&quot;604&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 url로 들어오는 요청은 프론트로, 도메인/api로 들어오는 요청은 백엔드로 보내고 있고,&amp;nbsp; jenkins는 자바 기반 웹 애플리케이션으로 localhost:8080이 시작점이지만, docker로 실행해 host 8082 포트와 jenkins container 8080와 포트포워딩을 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인으로 접근하지만 jenkins에 요청을 하기 위해서는 두가지 방법이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. nginx 에서 jenkins로 시작하는 요청을 rewrite 후 localhost:8082로 프록시 패스&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. jenkins 서버에 prefix jenkins를 붙이고 nginx에서 localhost:8082/jenkins로 프록시 패스&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;첫번째 방법은 왠지 모르지만 접근하면 nginx 404 not Found 에러가 발생해서 ... 두번째 방법으로 해결하는 법 포스팅하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Jenkins Prefix 추가&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 도커&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;젠킨스를 도커로 아직 실행하지 않았다면 실행하면 끝입니다. Nginx Jenkins Proxy 부분으로 넘어가세요&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722404069311&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -e JENKINS_OPTS=&quot;--prefix=/jenkins&quot; \
  jenkins/jenkins:lts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 로컬, 이미 실행중인 도커 컨테이너&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;저는 이미 실행중인 도커 컨테이너에 적용해야 해서 해당 방법을 적용했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722404265834&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -it -u root jenkins-container /bin/bash

cd /var/jenkins_home

echo &quot;JENKINS_OPTS=\&quot;--prefix=/jenkins\&quot;&quot; &amp;gt;&amp;gt; jenkins.properties

#vi, vim, nano등의 편집기 없다면
apt-get update
apt-get install -y nano

vi /usr/local/bin/jenkins.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. jenkins.sh 스크립트 파일에 설정 내용 읽는 부분 추가&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722404290492&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if [ -f /var/jenkins_home/jenkins.properties ]; then
  . /var/jenkins_home/jenkins.properties
fi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. docker or jenkins 재실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722404344605&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker restart jenkins-container
systemctl restart jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx Jenkins Proxy&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;/etc/nginx/nginx.conf 나 활용중인 site-available 내에 conf 파일에 jenkins 프록시 설정을 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722404448163&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# jenkins upstream 설정
upstream jenkins {
  keepalive 32; # keepalive connections
  server 127.0.0.1:8082; # jenkins ip and port
}
# nginx websocket 활용 설정
 map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }
server {
    server_name domain;
    listen 443 ssl;

     ssl_certificate /etc/letsencrypt/live/domain/fullchain.pem; 
     ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem; 

    location /api { 
        proxy_pass http://localhost:8080;
        proxy_set_header X-Real-IP $remote_addr;
        error_page 405 = $uri;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
	
    # location jenkins
    location /jenkins {
      sendfile off;
      proxy_pass         http://jenkins;
      proxy_redirect     default;
      proxy_http_version 1.1;

      # Required for Jenkins websocket agents
      proxy_set_header   Connection        $connection_upgrade;
      proxy_set_header   Upgrade           $http_upgrade;

      proxy_set_header   Host              $http_host;
      proxy_set_header   X-Real-IP         $remote_addr;
      proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
      proxy_set_header   X-Forwarded-Proto $scheme;
      proxy_max_temp_file_size 0;

      #this is the maximum upload size
      client_max_body_size       10m;
      client_body_buffer_size    128k;

      proxy_connect_timeout      90;
      proxy_send_timeout         90;
      proxy_read_timeout         90;
      proxy_request_buffering    off; # Required for HTTP CLI commands
}

     location / { 
         root /var/www/html;
	}
}

server { 
    if ($host = domain) { 
        return 301 https://$host$request_uri;
    } # managed by Certbot

  listen 80;
  server_name domain;
    return 404; # managed by Certbot
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx test &amp;amp; Reload&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1722404533884&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo nginx -t 

sudo nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Https 적용과 jenkins로 시작하는 prefix가 적용되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_jenkins.png&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW3Pmv/btsIReOVHW1/9fLo6mbyeWS1KvzyMv0X6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW3Pmv/btsIReOVHW1/9fLo6mbyeWS1KvzyMv0X6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW3Pmv/btsIReOVHW1/9fLo6mbyeWS1KvzyMv0X6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW3Pmv%2FbtsIReOVHW1%2F9fLo6mbyeWS1KvzyMv0X6k%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;1249&quot; height=&quot;370&quot; data-filename=&quot;edited_jenkins.png&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;370&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/reverse-proxy-configuration-nginx/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/reverse-proxy-configuration-nginx/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722405299447&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;Reverse proxy - Nginx&quot; data-og-description=&quot;Jenkins &amp;ndash; an open source automation server which enables developers around the world to reliably build, test, and deploy their software&quot; data-og-host=&quot;www.jenkins.io&quot; data-og-source-url=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/reverse-proxy-configuration-nginx/&quot; data-og-url=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/reverse-proxy-configuration-nginx/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bN7Jgh/hyWGTXE1A0/V3ft7tizJaABK4FdOCHp80/img.png?width=1200&amp;amp;height=630&amp;amp;face=581_198_680_306&quot;&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/reverse-proxy-configuration-nginx/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/reverse-proxy-configuration-nginx/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bN7Jgh/hyWGTXE1A0/V3ft7tizJaABK4FdOCHp80/img.png?width=1200&amp;amp;height=630&amp;amp;face=581_198_680_306');&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;Reverse proxy - Nginx&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Jenkins &amp;ndash; an open source automation server which enables developers around the world to reliably build, test, and deploy their software&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.jenkins.io&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;</description>
      <category>개발로그</category>
      <category>Jenkins</category>
      <category>Nginx</category>
      <category>Reverse Proxy</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/198</guid>
      <comments>https://anythingis.tistory.com/198#entry198comment</comments>
      <pubDate>Wed, 31 Jul 2024 14:50:12 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] Zipkin &amp;amp; Micrometer</title>
      <link>https://anythingis.tistory.com/197</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Trace Id, Span Id&lt;/span&gt;&lt;/h2&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;1243&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F8WU8/btsISJfFBMC/KeCefyMLu1GHaE5gJnckKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F8WU8/btsISJfFBMC/KeCefyMLu1GHaE5gJnckKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F8WU8/btsISJfFBMC/KeCefyMLu1GHaE5gJnckKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF8WU8%2FbtsISJfFBMC%2FKeCefyMLu1GHaE5gJnckKK%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;1243&quot; height=&quot;386&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 요청에서 여러 마이크로 서비스의 참조가 필요할 때 전체적으로 &lt;i&gt;&lt;b&gt;동일한 trace Id&lt;/b&gt;&lt;/i&gt;를 사용하고, &lt;i&gt;&lt;b&gt;서비스마다 다른 sapn Id&lt;/b&gt;&lt;/i&gt;를 사용합니다. 이를 통해 문제가 있는 trace Id 요청을 트레이싱해서 문제가 있는 서비스를 찾을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Micrometer&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;SpringBoot 2에서는 spring cloud sleuth를 통해 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;spring boot에서 zipkin으로 span id, trace id 전송해&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;분산 추적을 진행했는데&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1mDrV/btsIKBcNCAR/Zr65GRMzbrWu1aOvSPDJLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1mDrV/btsIKBcNCAR/Zr65GRMzbrWu1aOvSPDJLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1mDrV/btsIKBcNCAR/Zr65GRMzbrWu1aOvSPDJLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1mDrV%2FbtsIKBcNCAR%2FZr65GRMzbrWu1aOvSPDJLk%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;1280&quot; height=&quot;643&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이와 같이 순환참조가 일어나는 구조와 Spring과 SpringBoot에서는 트레이싱 기능일 지원하지 않는 구조로 인해&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; SpringBoot 3에서는 Sleuth의 트레이싱 기능이 Micrometer로 이관되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://techblog.lycorp.co.jp/ko/how-to-migrate-to-spring-boot-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://techblog.lycorp.co.jp/ko/how-to-migrate-to-spring-boot-3&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721806515167&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;실전! Spring Boot 3 마이그레이션&quot; data-og-description=&quot;들어가며 안녕하세요. LINE Plus에서 태국 LINE BK 채널 서버 개발 및 운영 업무를 맡고 있는 이석재입니다. LINE BK에서는 지난 9월에 LINE BK 보험 중개 서...&quot; data-og-host=&quot;techblog.lycorp.co.jp&quot; data-og-source-url=&quot;https://techblog.lycorp.co.jp/ko/how-to-migrate-to-spring-boot-3&quot; data-og-url=&quot;https://techblog.lycorp.co.jp/ko/how-to-migrate-to-spring-boot-3&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cEKLp5/hyWCJ1Yj0p/EGJG7Z5cmRs2SQcjOMuaX0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/eHAf1C/hyWCDUYPts/H5kV5K39hQu3XHzSY7UZm0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/bs5VqQ/hyWCMRWTBn/rIFuHcfc0z4X2vhq0tTtx1/img.png?width=3244&amp;amp;height=1632&amp;amp;face=0_0_3244_1632&quot;&gt;&lt;a href=&quot;https://techblog.lycorp.co.jp/ko/how-to-migrate-to-spring-boot-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://techblog.lycorp.co.jp/ko/how-to-migrate-to-spring-boot-3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cEKLp5/hyWCJ1Yj0p/EGJG7Z5cmRs2SQcjOMuaX0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/eHAf1C/hyWCDUYPts/H5kV5K39hQu3XHzSY7UZm0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/bs5VqQ/hyWCMRWTBn/rIFuHcfc0z4X2vhq0tTtx1/img.png?width=3244&amp;amp;height=1632&amp;amp;face=0_0_3244_1632');&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;실전! Spring Boot 3 마이그레이션&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가며 안녕하세요. LINE Plus에서 태국 LINE BK 채널 서버 개발 및 운영 업무를 맡고 있는 이석재입니다. LINE BK에서는 지난 9월에 LINE BK 보험 중개 서...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;techblog.lycorp.co.jp&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;zipkin&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;micrometer로 생성한 trace Id, span Id 정보를 저장하고 마이크로 서비스별, trace Id 별 검색을 통해 서비스 흐름을 시각화합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;zipkin 실행&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721804394218&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d -p 9411:9411--restart=always openzipkin/zipkin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존성 추가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;actuator 의존성을 추가해야 tracing, zipkin 관련 정보에 접근할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721804699347&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// actuator 의존성 추가해야 tracing, zipkin 관련 활용 가능
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'

implementation group: 'io.micrometer', name: 'micrometer-observation'

implementation group: 'io.micrometer', name: 'micrometer-tracing-bridge-brave'

implementation group: 'io.zipkin.brave', name: 'brave-instrumentation-spring-web'

implementation group: 'io.zipkin.reporter2', name: 'zipkin-reporter-brave'

implementation group: 'io.github.openfeign', name: 'feign-micrometer'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml 설정&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721882861831&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  application:
    name: user-service
  zipkin:
    base-url: http://localhost:9411
    enabled: true
    
management:
  tracing:
    sampling:
      probability: 1.0
    propagation:
      consume: B3
      produce: B3_MULTI
  zipkin:
    tracing:
      endpoint: &quot;http://localhost:9411/api/v2/spans&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 연쇄되는 API 호출&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;user service에서 사용자의 주문 목록을 조회하기 위해 order service까지 호출하는 사용자 정보 API 호출을 해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/smfO8/btsIMQGPf1o/sWk6hv8A1ofEhKekd95t3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/smfO8/btsIMQGPf1o/sWk6hv8A1ofEhKekd95t3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/smfO8/btsIMQGPf1o/sWk6hv8A1ofEhKekd95t3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsmfO8%2FbtsIMQGPf1o%2FsWk6hv8A1ofEhKekd95t3k%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;1170&quot; height=&quot;383&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;383&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;1452&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eoiyQH/btsINZv5pYV/bKNTTjQghYQ4SJ4hbXbM60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eoiyQH/btsINZv5pYV/bKNTTjQghYQ4SJ4hbXbM60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eoiyQH/btsINZv5pYV/bKNTTjQghYQ4SJ4hbXbM60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeoiyQH%2FbtsINZv5pYV%2FbKNTTjQghYQ4SJ4hbXbM60%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;1452&quot; height=&quot;56&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;56&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;&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;1917&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qqIgz/btsINZJOrOr/YPZWPe5kOgZCDJ3zsfEbK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qqIgz/btsINZJOrOr/YPZWPe5kOgZCDJ3zsfEbK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qqIgz/btsINZJOrOr/YPZWPe5kOgZCDJ3zsfEbK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqqIgz%2FbtsINZJOrOr%2FYPZWPe5kOgZCDJ3zsfEbK0%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;860&quot; height=&quot;278&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;620&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;1905&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l17Y2/btsINsyJmw8/WeDl7XC83yFNrJyqnvnaJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l17Y2/btsINsyJmw8/WeDl7XC83yFNrJyqnvnaJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l17Y2/btsINsyJmw8/WeDl7XC83yFNrJyqnvnaJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl17Y2%2FbtsINsyJmw8%2FWeDl7XC83yFNrJyqnvnaJ1%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;1905&quot; height=&quot;576&quot; data-origin-width=&quot;1905&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/197</guid>
      <comments>https://anythingis.tistory.com/197#entry197comment</comments>
      <pubDate>Thu, 25 Jul 2024 14:43:03 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] CircuitBreaker &amp;amp; Resilience4J</title>
      <link>https://anythingis.tistory.com/196</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CircuitBreaker는 왜 필요한가?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 별로 서비스(서버)가 나뉘어지는 MSA 아키텍처에서는 특정 유저의 주문 리스트를 조회하는 경우에 다음과 같은 순서로 이루어 집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1323&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crydSo/btsIKTDXt6C/SFKwai0J4p6v0iuN9lO1dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crydSo/btsIKTDXt6C/SFKwai0J4p6v0iuN9lO1dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crydSo/btsIKTDXt6C/SFKwai0J4p6v0iuN9lO1dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrydSo%2FbtsIKTDXt6C%2FSFKwai0J4p6v0iuN9lO1dk%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;1323&quot; height=&quot;470&quot; data-origin-width=&quot;1323&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. user-service 호출&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721799650595&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; user-service / controller
 @GetMapping(&quot;/users/{userId}&quot;)
    public ResponseEntity&amp;lt;ResponseUser&amp;gt; getUser(@PathVariable String userId){
        UserDto userDto = userService.getUserByUserId(userId);

        return ResponseEntity.status(HttpStatus.OK)
                .body(new ModelMapper().map(userDto, ResponseUser.class));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. order-service 호출&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721799677052&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user-service / service
@Override
    public UserDto getUserByUserId(String userId) {
        UserEntity userEntity = userRepository.findByUserId(userId);

        if(userEntity == null)throw new UsernameNotFoundException(&quot;User Not Found&quot;);

        UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);

        // TODO order service 구현
        List&amp;lt;ResponseOrder&amp;gt; orders = orderServiceClient.getOrders(userId);

        userDto.setOrders(orders);

        return userDto;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721799716501&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;order-service&quot;)
public interface OrderServiceClient {
    @GetMapping(&quot;/order-service/{userId}/orders&quot;)
     List&amp;lt;ResponseOrder&amp;gt; getOrders(@PathVariable String userId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. order-service 반환&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. user-service 반환&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z1fkO/btsIKV2Ri3e/GFBRDgDbFk5YYAIW8Eo1BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z1fkO/btsIKV2Ri3e/GFBRDgDbFk5YYAIW8Eo1BK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z1fkO/btsIKV2Ri3e/GFBRDgDbFk5YYAIW8Eo1BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ1fkO%2FbtsIKV2Ri3e%2FGFBRDgDbFk5YYAIW8Eo1BK%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;1332&quot; height=&quot;520&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때 만약 order service가 다운되었거나 기동이 안되어 있으면 user-service까지 에러가 전파되어 500 서버 에러가 반환될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjvVia/btsILjvASZ8/bbjWIlXyltRZkRkvf4qYV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjvVia/btsILjvASZ8/bbjWIlXyltRZkRkvf4qYV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjvVia/btsILjvASZ8/bbjWIlXyltRZkRkvf4qYV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjvVia%2FbtsILjvASZ8%2FbbjWIlXyltRZkRkvf4qYV1%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;1152&quot; height=&quot;744&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;744&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1721799572390&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.net.UnknownHostException: order-service
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:567) ~[na:na]
	at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:178) ~[na:na]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런 경우 문제가 있는 서비스에 호출을 하지 않고 기본값이나 우회할 수 있게 예방해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Feign Client의 ErrorDocder는..?&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721798743082&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class FeignErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        switch (response.status()){
            case 400:
                break;
            case 404:
                if(methodKey.contains(&quot;getOrders&quot;)){
                    return new ResponseStatusException(HttpStatusCode.valueOf(response.status()),
                            &quot;User's order is empty&quot;);
                }
                break;
            default:
                return new Exception(response.reason());
        }

        return null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Feign Client의 ErrorDecoder를 통해 500에러를 404에러로 바꿨습니다. 호출하는 서비스가 기동중이지만 에러가 발생했을 때는 ErrorDecoder가 동작하지만 호출 서비스 자체가 다운되면 에러가 전파됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CircuitBreaker&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;circuitBreaker를 도입하면 평소에는 정상적으로 동작하고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM8JDo/btsIMGcmsDF/6MwdpVpc4F2GCCSL3rHQaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM8JDo/btsIMGcmsDF/6MwdpVpc4F2GCCSL3rHQaK/img.png&quot; data-alt=&quot;order servic 정상 동작&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM8JDo/btsIMGcmsDF/6MwdpVpc4F2GCCSL3rHQaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM8JDo%2FbtsIMGcmsDF%2F6MwdpVpc4F2GCCSL3rHQaK%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;990&quot; height=&quot;221&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;order servic 정상 동작&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;호출하는 서비스가 불안정하거나, 다운되면 설정한 기본값을 제공해 에러 전파를 막을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZzKWV/btsIKKm0ucF/HqQd2X45A3397cXAPSyJP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZzKWV/btsIKKm0ucF/HqQd2X45A3397cXAPSyJP1/img.png&quot; data-alt=&quot;order service down&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZzKWV/btsIKKm0ucF/HqQd2X45A3397cXAPSyJP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZzKWV%2FbtsIKKm0ucF%2FHqQd2X45A3397cXAPSyJP1%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;1041&quot; height=&quot;261&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;order service down&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CircuitBreaker 설정 &amp;amp; 과정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2018년 까진 hetrix를 활용했지만 최근에는 resilience4J가 그 자리를 대체합니다.&amp;nbsp;&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존성 추가&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1721800143888&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-circuitbreaker-resilience4j'&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설정 추가&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1721803443209&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class Resilience4JConfig {
    @Bean
    public Customizer&amp;lt;Resilience4JCircuitBreakerFactory&amp;gt; globalCustomConfiguration(){
        CircuitBreakerConfig circuitBreakConfig = CircuitBreakerConfig.custom()
                .failureRateThreshold(4) // 실패율, 100번에 4번 실패하면 circuitbreaker open
                .waitDurationInOpenState(Duration.ofMillis(1000)) // open 상태 유지하는 시간, 이후는 half-oepn 상태로 진입
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) // close 될때 통화 결과 기록(카운트 기반, 시간 기반)
                .slidingWindowSize(2).build();

        TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
                .timeoutDuration(Duration.ofSeconds(4)) // 호출 대기하는 time limit, limit 초과시 circuitbreaker open
                .build();
        return factory -&amp;gt; factory.configureDefault(id -&amp;gt;
                new Resilience4JConfigBuilder(id)
                        .timeLimiterConfig(timeLimiterConfig)
                        .circuitBreakerConfig(circuitBreakConfig)
                        .build());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 외부 서비스 호출할 때 문제가 없다면 CircuitBreaker는 close 진입&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. timelimit을 초과하거나, 실패율을 초과하면 open 상태에 진입&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. open 유지 시간이 지난 뒤에 half-open 상태에 진입해 다시 외부 서비스를 호출 결과를 통해 open, close에 진입&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CircuitBreaker 구현&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721800957615&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.user.service;

import com.example.user.client.OrderServiceClient;
import com.example.user.dto.UserDto;
import com.example.user.error.FeignErrorDecoder;
import com.example.user.jpa.UserEntity;
import com.example.user.jpa.UserRepository;
import com.example.user.vo.ResponseOrder;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService{
    ....
    // circuitBreakerFactory 주입
    private final CircuitBreakerFactory circuitBreakerFactory;
	
    @Override
    public UserDto getUserByUserId(String userId) {
        UserEntity userEntity = userRepository.findByUserId(userId);

        if(userEntity == null)throw new UsernameNotFoundException(&quot;User Not Found&quot;);

        UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);

        // Fegin Client 자체 이용
//        List&amp;lt;ResponseOrder&amp;gt; orders = orderServiceClient.getOrders(userId);
		
        
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create(&quot;circuitbreaker&quot;);
        // CircuitBreak 감싼 Feign Client 이용
        List&amp;lt;ResponseOrder&amp;gt; orders = circuitBreaker.run(() -&amp;gt; orderServiceClient.getOrders(userId),
                throwable -&amp;gt; new ArrayList&amp;lt;&amp;gt;());

        userDto.setOrders(orders);

        return userDto;
    }




}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Order Serivce가 down되어 있어도 fallback method를 통해 빈 List를 반환해 User Service 까지 에러가 전파되지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v0cEt/btsILHJNr2E/821OXfZQERniiHNovsWlB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v0cEt/btsILHJNr2E/821OXfZQERniiHNovsWlB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v0cEt/btsILHJNr2E/821OXfZQERniiHNovsWlB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv0cEt%2FbtsILHJNr2E%2F821OXfZQERniiHNovsWlB0%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;1174&quot; height=&quot;375&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그</category>
      <category>circuitbreaker</category>
      <category>MSA</category>
      <category>resilience4j</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/196</guid>
      <comments>https://anythingis.tistory.com/196#entry196comment</comments>
      <pubDate>Wed, 24 Jul 2024 15:52:12 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] INSERT Query 스프링에서 Kafka로 위임하기</title>
      <link>https://anythingis.tistory.com/195</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/193&quot;&gt;https://anythingis.tistory.com/193&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721715741804&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;[MSA] Apache Kafka &amp;amp; Kafka Connect&quot; data-og-description=&quot;KafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있을 수 있지만 아래와 같은 단점이 있습니다.&amp;nbsp;데이터 연동의&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/193&quot; data-og-url=&quot;https://anythingis.tistory.com/193&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/f6eBa/hyWCA4QrAq/ZZ9sOA6If35VikTYje9Cck/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/zz0zC/hyWCLFii0c/X1bURaikK8oLXTUEbQ4s81/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/ld3JV/hyWCIPkTpX/cSKwxPcT8kYHXem5ODzfgK/img.png?width=939&amp;amp;height=450&amp;amp;face=647_90_724_365&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/193&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/f6eBa/hyWCA4QrAq/ZZ9sOA6If35VikTYje9Cck/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/zz0zC/hyWCLFii0c/X1bURaikK8oLXTUEbQ4s81/img.png?width=512&amp;amp;height=234&amp;amp;face=0_0_512_234,https://scrap.kakaocdn.net/dn/ld3JV/hyWCIPkTpX/cSKwxPcT8kYHXem5ODzfgK/img.png?width=939&amp;amp;height=450&amp;amp;face=647_90_724_365');&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;[MSA] Apache Kafka &amp;amp; Kafka Connect&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;KafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있을 수 있지만 아래와 같은 단점이 있습니다.&amp;nbsp;데이터 연동의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 포스팅에서 Zookeeper, Kafka, Kafka Connect를 활용해봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G7tYP/btsIKCO442z/WHSk40XmkmILEOINQsWqGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G7tYP/btsIKCO442z/WHSk40XmkmILEOINQsWqGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G7tYP/btsIKCO442z/WHSk40XmkmILEOINQsWqGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG7tYP%2FbtsIKCO442z%2FWHSk40XmkmILEOINQsWqGK%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;689&quot; height=&quot;402&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그중에서도 단일 DB를 활용하고, 서비스가 DB에 INSERT를 직접 하는게 아닌 카프카에 위임하는 방법을 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장점 :&amp;nbsp;서비스가 DB Lock, Connection에 대해 자유로움, 동시성 처리를 Kafka가, 비즈니스 로직을 서비스가 분담&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단점 : &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;서비스가 직접 커넥션을 물고 있는게 아니라 예외 발생시 보상처리를 위한 사가패턴이 필요&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;환경설정&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;maria db create orders&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721628335618&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create table orders (

  id int auto_increment primary key,

  user_id varchar(50) not null,

  product_id varchar(20) not null,

  order_id varchar(50) not null,

  qty int default 0,

  unit_price int default 0,

  total_price int default 0,

  created_at datetime default now()

)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;인메모리 h2가 아닌 실제 DB에 연결해 다수의 서비스에서 동일한 DB에 접근하게 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;INSERT 쿼리는 카프카가 전담하기에 SELECT 쿼리를 위해 연결합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721628573399&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    #driver-class-name: org.h2.Driver
    #url: jdbc:h2:mem:testdb
    driver-class-name: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://localhost:3306/mydb
    username: root
    password: 1q2w3e4r
    generate-unique-name: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Order Service INSERT 쿼리 카프카에 위임&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Controller + Service&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스가 직접 JPA를 활용해 DB에 접근하는 과정을 카프카 Produce 과정으로 변환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래는 기존 order service의 serviceImpl 구현로직입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; order service에서 담당하던 total Price를 구하고 DB에 저장하는 로직을&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721630850604&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
    public OrderDto createOrder(OrderDto orderDetails) {
        orderDetails.setOrderId(UUID.randomUUID().toString());
        orderDetails.setTotalPrice(orderDetails.getQty() * orderDetails.getUnitPrice());

        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

        OrderEntity orderEntity = mapper.map(orderDetails, OrderEntity.class);

        orderRepository.save(orderEntity);
        return mapper.map(orderEntity, OrderDto.class);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컨트롤러에서 구현하고 INSERT는 Kafka Producer로 처리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721630835671&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; @PostMapping(&quot;/{userId}/orders&quot;)
    public ResponseEntity&amp;lt;ResponseOrder&amp;gt; createOrder(@RequestBody RequestOrder order,
                                                     @PathVariable String userId) {

        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

        OrderDto orderDto = mapper.map(order, OrderDto.class);
        orderDto.setUserId(userId);
        /*
        jpa
        OrderDto returnUserDto = orderService.createOrder(orderDto);
        ResponseOrder responseOrder = mapper.map(returnUserDto, ResponseOrder.class);
        */

        //kafka
        orderDto.setOrderId(UUID.randomUUID().toString());
        orderDto.setTotalPrice(order.getQty() * order.getUnitPrice());
        ResponseOrder responseOrder = mapper.map(orderDto, ResponseOrder.class);

        kafkaProducer.send(&quot;example-catalog-topic&quot;, orderDto);
        // kafka
        orderProducer.send(&quot;orders&quot;, orderDto);
        return new ResponseEntity(HttpStatus.CREATED).ok(responseOrder);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;OrderProducerKafka&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;INSERT 쿼리를 카프카에 Produce하기 위한 Producer를 구현합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;i&gt;DTO 타입과 DB타입이 매칭 되지 않으면 DB에 null 값이 들어감으로 주의하세요.&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721630934024&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@Slf4j
@RequiredArgsConstructor
public class OrderProducer {
    private final KafkaTemplate&amp;lt;String, String&amp;gt; kafkaTemplate;

    List&amp;lt;Field&amp;gt; fields = Arrays.asList(new Field(&quot;string&quot;, true, &quot;order_id&quot;),
            new Field(&quot;string&quot;, true, &quot;user_id&quot;),
            new Field(&quot;string&quot;, true, &quot;product_id&quot;),
            new Field(&quot;int32&quot;, true, &quot;qty&quot;), // DTO 타입 - DB 타입 매칭 중요
            new Field(&quot;int32&quot;, true, &quot;unit_price&quot;),
            new Field(&quot;int32&quot;, true, &quot;total_price&quot;));
    Schema schema = Schema.builder()
            .type(&quot;struct&quot;)
            .optional(false)
            .name(&quot;orders&quot;)
            .fields(fields).build();
    public OrderDto send(String topic, OrderDto orderDto){
        Payload payload = Payload.builder().
                order_id(orderDto.getOrderId())
                .user_id(orderDto.getUserId())
                .product_id(orderDto.getProductId())
                .qty(orderDto.getQty())
                .unit_price(orderDto.getUnitPrice())
                .total_price(orderDto.getTotalPrice())
                .build();

        KafkaOrderDto kafkaOrderDto = new KafkaOrderDto(schema, payload);

        ObjectMapper mapper = new ObjectMapper();
        String jsonInString = &quot;&quot;;
        try {
            jsonInString = mapper.writeValueAsString(kafkaOrderDto);
        }catch (JsonProcessingException e){
            e.printStackTrace();
        }

        kafkaTemplate.send(topic, jsonInString);
        log.info(&quot;Order Producer send data from Order microservice: &quot; + kafkaOrderDto);

        return orderDto;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실행&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Zookeeper 실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721715762927&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd /kafka_2.12-3.4.0
./bin/windows/zookeeper-server-start.bat ./config/zookeeper.properties # windows 기준&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka 실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721715772959&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./bin/windows/kafka-server-start.bat ./config/server.properties&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect 실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721715788921&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd confluent-7.3.1
./bin/windows/connect-distributed.bat ./etc/kafka/connect-distributed.properties #windows 기준&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Order Sink Connect 추가&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721631566483&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST http://localhost:8083/connectors

{
    &quot;name&quot;: &quot;my-order-sink-connect&quot;,
    &quot;config&quot;: {
        &quot;connector.class&quot;: &quot;io.confluent.connect.jdbc.JdbcSinkConnector&quot;,
        &quot;connection.url&quot;: &quot;jdbc:mariadb://localhost:3306/mydb&quot;,
        &quot;connection.user&quot;: &quot;root&quot;,
        &quot;connection.password&quot;: &quot;1q2w3e4r&quot;,
        &quot;auto.create&quot;: &quot;true&quot;,
        &quot;auto.evolve&quot;: &quot;true&quot;,
        &quot;delete.enabled&quot;: &quot;false&quot;,
        &quot;tasks.max&quot;: &quot;1&quot;,
        &quot;topics&quot;: &quot;orders&quot;
    }
}&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;781&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KDlie/btsIJB3opBI/y3Q2Me3LwGzc5sy2YdnjxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KDlie/btsIJB3opBI/y3Q2Me3LwGzc5sy2YdnjxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KDlie/btsIJB3opBI/y3Q2Me3LwGzc5sy2YdnjxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKDlie%2FbtsIJB3opBI%2Fy3Q2Me3LwGzc5sy2YdnjxK%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;781&quot; height=&quot;452&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;order service 실행&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;두개의 서비스를 기동합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;order api.png&quot; data-origin-width=&quot;1525&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH0qcR/btsIJCutKr5/DGkY56xsSqOwY1eGM5Rs0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH0qcR/btsIJCutKr5/DGkY56xsSqOwY1eGM5Rs0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH0qcR/btsIJCutKr5/DGkY56xsSqOwY1eGM5Rs0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH0qcR%2FbtsIJCutKr5%2FDGkY56xsSqOwY1eGM5Rs0K%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;1525&quot; height=&quot;533&quot; data-filename=&quot;order api.png&quot; data-origin-width=&quot;1525&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;ORDER POST 요청&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721716533232&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST http://localhost:8000/order-service/7fc99a15-44e0-456e-acdc-2d9cd00abda7/orders

{
    &quot;productId&quot; : &quot;CATALOG-005&quot;,
    &quot;qty&quot; : 10,
    &quot;unitPrice&quot; : 200
}

RESPONSE BODY
{
    &quot;productId&quot;: &quot;CATALOG-005&quot;,
    &quot;qty&quot;: 10,
    &quot;unitPrice&quot;: 200,
    &quot;totalPrice&quot;: 2000,
    &quot;orderId&quot;: &quot;bfb5a83f-9224-4e42-b252-79894c2e9215&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;POST 요청 서비스1, 2에 번갈아서 전달됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;order service 1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_order1.png&quot; data-origin-width=&quot;1835&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PmZ9d/btsIHfOt1Ix/RvRRVFWZoSJ0WxMMJHlbbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PmZ9d/btsIHfOt1Ix/RvRRVFWZoSJ0WxMMJHlbbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PmZ9d/btsIHfOt1Ix/RvRRVFWZoSJ0WxMMJHlbbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPmZ9d%2FbtsIHfOt1Ix%2FRvRRVFWZoSJ0WxMMJHlbbk%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;1835&quot; height=&quot;224&quot; data-filename=&quot;edited_order1.png&quot; data-origin-width=&quot;1835&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;order service 2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_order 2.png&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdJLUS/btsIJdPtxE8/x1z3I3MkycJMLNLfLuXpBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdJLUS/btsIJdPtxE8/x1z3I3MkycJMLNLfLuXpBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdJLUS/btsIJdPtxE8/x1z3I3MkycJMLNLfLuXpBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdJLUS%2FbtsIJdPtxE8%2Fx1z3I3MkycJMLNLfLuXpBK%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;1756&quot; height=&quot;100&quot; data-filename=&quot;edited_order 2.png&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;order service에서 OrderProducerKafka로 Produce 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;DB 확인&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단일 DB에 다수의 서비스를 에서 Kafka Connect Sink에 의해 DB에 Consume된 것을 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t83MK/btsIJYyirMG/KAiXdR2ZQsfJVrrvOj16L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t83MK/btsIJYyirMG/KAiXdR2ZQsfJVrrvOj16L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t83MK/btsIJYyirMG/KAiXdR2ZQsfJVrrvOj16L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft83MK%2FbtsIJYyirMG%2FKAiXdR2ZQsfJVrrvOj16L0%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;1116&quot; height=&quot;118&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그</category>
      <category>kafka</category>
      <category>kafka connect</category>
      <category>MSA</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/195</guid>
      <comments>https://anythingis.tistory.com/195#entry195comment</comments>
      <pubDate>Tue, 23 Jul 2024 15:38:40 +0900</pubDate>
    </item>
    <item>
      <title>[트러블슈팅] Kafka topic 내에 오류 데이터로 인해 kafka server 실행 안될 때</title>
      <link>https://anythingis.tistory.com/194</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;에러&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring 서버에서 produce 데이터를 Kafka로 보내고, Kafka Connect Sink를 활용해 DB에서 consume하려고 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래와 같은 데이터를 Serialize를 통해 Kafka로 보내려고 했는데 ...&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721712587093&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;schema&quot;: {
    &quot;type&quot;: &quot;struct&quot;,
    &quot;fields&quot;: [
      {
        &quot;type&quot;: &quot;int32&quot;,
        &quot;optional&quot;: false,
        &quot;field&quot;: &quot;id&quot;
      },
      {
        &quot;type&quot;: &quot;string&quot;,
        &quot;optional&quot;: true,
        &quot;field&quot;: &quot;user_id&quot;
      },
      {
        &quot;type&quot;: &quot;string&quot;,
        &quot;optional&quot;: true,
        &quot;field&quot;: &quot;pwd&quot;
      },
      {
        &quot;type&quot;: &quot;string&quot;,
        &quot;optional&quot;: true,
        &quot;field&quot;: &quot;name&quot;
      },
      {
        &quot;type&quot;: &quot;int64&quot;,
        &quot;optional&quot;: true,
        &quot;name&quot;: &quot;org.apache.kafka.connect.data.Timestamp&quot;,
        &quot;version&quot;: 1,
        &quot;field&quot;: &quot;created_at&quot;
      }
    ],
    &quot;optional&quot;: false,
    &quot;name&quot;: &quot;users&quot;
  },
  &quot;payload&quot;: {
    &quot;id&quot;: 1,
    &quot;user_id&quot;: &quot;users1&quot;,
    &quot;pwd&quot;: &quot;test1111&quot;,
    &quot;name&quot;: &quot;user name&quot;,
    &quot;created_at&quot;: 1721536482000
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;근데 아래와 같이 오타를 냈고 ...&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721712676169&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Data
@AllArgsConstructor
public class Field {
    private String type;
    private boolean optional;
    private String feild; // feild(x) -&amp;gt; field(o)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721713585281&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;because they are in the failed log directory E:\tmp\kafka-logs. (kafka.server.ReplicaManager)
[2024-07-23 14:08:54,119] INFO [ReplicaFetcherManager on broker 0] Removed fetcher for partitions Set(orders-0) (kafka.server.ReplicaFetcherManager)
[2024-07-23 14:08:54,119] WARN Stopping serving logs in dir E:\tmp\kafka-logs (kafka.log.LogManager)
[2024-07-23 14:08:54,119] INFO [ReplicaAlterLogDirsManager on broker 0] Removed fetcher for partitions Set(orders-0) (kafka.server.ReplicaAlterLogDirsManager)
[2024-07-23 14:08:54,122] ERROR Shutdown broker because all log dirs in E:\tmp\kafka-logs have failed (kafka.log.LogManager)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;잘못 만들어진 토픽 내 데이터로 인해 Connect Sink에서 오류가 났고, 카프카 서버 자체가 종료된 이후 에러 로그 때문에 다시 기동시켜도 켜지지 않았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그를 삭제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;토픽 내에 데이터를 삭제하기 위해 config/server.properties로 로그 경로를 확인하고 삭제했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOYvhp/btsIJwWtABJ/ssrcXaqePSocpKgXYYKkGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOYvhp/btsIJwWtABJ/ssrcXaqePSocpKgXYYKkGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOYvhp/btsIJwWtABJ/ssrcXaqePSocpKgXYYKkGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOYvhp%2FbtsIJwWtABJ%2FssrcXaqePSocpKgXYYKkGk%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;525&quot; height=&quot;187&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;orders 토픽이 문제라 orders-0, orders-1 ... 등의 모든 폴더를 삭제했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 해결이 되지 않아 도커로 카프카를 기동시켜야 하나 고민했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;zookeeper 메타데이터 삭제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주키퍼는 작동이 되었고, 주키퍼 내에 카프카의 메타데이터를 저장해서 카프카 서버가 기동될 때 해당 정보를 가져와서 에러가 발생하는 것 같았습니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 카프카 서버 자체가 기동되지 않아 bin/kafka-topics.bat을 실행시켜 topic을 삭제할 수 없었습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그나마 작동이 되던 &lt;b&gt;주키퍼와 주키퍼 쉘을 통해 토픽을 삭제&lt;/b&gt;하는 방법을 알아봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721713262549&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd kafka_2.12-3.4.0

# 주키퍼 기동
./bin/windows/zookeeper-server-start.bat ./config/zookeeper.properties 

# 주키퍼 쉘 진입
./zookeeper-shell.bat localhost:2181

# 토픽 리스트 확인
ls /brokers/topics

# 토픽 삭제
deleteall /brokers/topics/orders

# 토픽 리스트 확인 - 삭제 토픽 확인
ls /brokers/topics&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쉘이 이상한건지.. 동일한 명령어도 안되다가 엔터 몇번 치고 입력햇을 땐 됐습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후 쉘을 빠져나와서 주키퍼와 카프카를 다시 기동하면 문제 있던 토픽이 삭제되어 정상적으로 동작합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카프카 토픽 내의 데이터를 로그로 관리한다는 것과 주키퍼가 카프카 메타데이터를 관리한다는 것을 배울 수 있었습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발로그</category>
      <category>kafka</category>
      <category>Topic</category>
      <category>zookeeper</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/194</guid>
      <comments>https://anythingis.tistory.com/194#entry194comment</comments>
      <pubDate>Tue, 23 Jul 2024 14:45:00 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] Apache Kafka &amp;amp; Kafka Connect</title>
      <link>https://anythingis.tistory.com/193</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka 개요&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRVLBw/btsIHCBQH8a/F29E7eyrGsJN8cgARzcZm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRVLBw/btsIHCBQH8a/F29E7eyrGsJN8cgARzcZm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRVLBw/btsIHCBQH8a/F29E7eyrGsJN8cgARzcZm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRVLBw%2FbtsIHCBQH8a%2FF29E7eyrGsJN8cgARzcZm0%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;562&quot; height=&quot;318&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있을 수 있지만 아래와 같은 단점이 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;데이터 연동의 복잡성이 증가해 하드웨어, 운영체제에 영향을 많이 받고 장애에 영향을 많이 받음&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;서로 다른 데이터 파이프 라인으로 인한 확장에 어려움&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TtS5g/btsIG3tcoh2/TUYSdRhiyIh8ZEoriRIiH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TtS5g/btsIG3tcoh2/TUYSdRhiyIh8ZEoriRIiH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TtS5g/btsIG3tcoh2/TUYSdRhiyIh8ZEoriRIiH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTtS5g%2FbtsIG3tcoh2%2FTUYSdRhiyIh8ZEoriRIiH0%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;588&quot; height=&quot;421&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이를 해결하고자 중간단계의 추상화를 위해 Apache Kafka가 등장했고, Data를 생성하는 &lt;i&gt;&lt;b&gt;Producer&lt;/b&gt;&lt;/i&gt;, Data를 소비하는 &lt;i&gt;&lt;b&gt;Cosumer&lt;/b&gt;&lt;/i&gt;를 통해 end-to-end는 서로 모르지만, 유연한 데이터 파이프라인을 구축할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;kafka 구성&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HWdOf/btsIGHqyJcb/wMCn2lH4Cwnu7nDGDcwdfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HWdOf/btsIGHqyJcb/wMCn2lH4Cwnu7nDGDcwdfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HWdOf/btsIGHqyJcb/wMCn2lH4Cwnu7nDGDcwdfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHWdOf%2FbtsIGHqyJcb%2FwMCn2lH4Cwnu7nDGDcwdfk%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;876&quot; height=&quot;453&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;kafka Broker&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제로 &lt;b&gt;실행되는 Kafka 애플리케이션 서버&lt;/b&gt;로 3대 이상의 Broker Cluster를 구성해 브로커에 이상이 있을 시 다른 Broker를 활용하는 등의 클러스터링이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;N개의 Broker 중 한대는 Controller 기능을 수행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각 Broker에게 담당 파티션 할당 수행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Broker 정상 동작 모니터링 관리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Apache Zookeeper&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메타데이터 (Broker ID, Controller Id) 저장, Controller 정보 저장하고,&amp;nbsp;저장된 정보를 통해 Controller에 이상이 생겼을 때 정상적인 &lt;b&gt;Broker를 Controller로 승격을 관리&lt;/b&gt;합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MySQL Duplication 대신 Kafka를 알았더라면...?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 프로젝트에서 DB 백업과 INSERT와 SELECT 하는 DB를 나눠 성능향상을 위해 MySQL Duplication을 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/174&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/174&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721540464494&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;[프로젝트 회고] 시작 프로젝트 회고&quot; data-og-description=&quot;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file GitHub - kanggeonnim/PerspectiView: 시점으로 보는 작품, 시작 시점으로 보는 작품, 시작. Contribute to kanggeonnim/PerspectiView development by creating an account on GitHub.&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/174&quot; data-og-url=&quot;https://anythingis.tistory.com/174&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bebTf9/hyWCAb49bI/MG8LcNw9Xn0UAxm2cWhq70/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449,https://scrap.kakaocdn.net/dn/bCq9l8/hyWCEZT4Yz/FBLkkD4XXLjCRKjLd0cPp0/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449,https://scrap.kakaocdn.net/dn/bPBGH2/hyWCz5kQtp/n3NVjaiiYoKhGt6XWvNY91/img.png?width=1608&amp;amp;height=629&amp;amp;face=0_0_1608_629&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/174&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/174&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bebTf9/hyWCAb49bI/MG8LcNw9Xn0UAxm2cWhq70/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449,https://scrap.kakaocdn.net/dn/bCq9l8/hyWCEZT4Yz/FBLkkD4XXLjCRKjLd0cPp0/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449,https://scrap.kakaocdn.net/dn/bPBGH2/hyWCz5kQtp/n3NVjaiiYoKhGt6XWvNY91/img.png?width=1608&amp;amp;height=629&amp;amp;face=0_0_1608_629');&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;[프로젝트 회고] 시작 프로젝트 회고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file GitHub - kanggeonnim/PerspectiView: 시점으로 보는 작품, 시작 시점으로 보는 작품, 시작. Contribute to kanggeonnim/PerspectiView development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 부분 맡은 팀원이 mysql 복제를 위해 별로 없는 레퍼런스를 찾아가며 설정과 테스트를 위해 일주일정도의 걸렸고, 이 경험이 나중에 프로젝트에서 MySQL를 사용할 때 유의미하다고 생각해 아쉬웠습니다. Kafka를 활용하면 Producer/Consumer를 분리해 &lt;b&gt;운영체제, 서비스에 구애받지 않는 파이프라인을 구축&lt;/b&gt;하는 역량을 길러 &lt;b&gt;다른 서비스끼리 연동할 때도 활용&lt;/b&gt;할 수 있어 더 좋았을 것 같습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(Kafka Broker, Zookeeper 등으로 인한 기본으로 구동되는 무거운 서버 부하에 대한 부분도 생각해야할 듯 합니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Producer와 Consumer로 Kafka Pipeline을 생성할 수 있지만 반복적으로 파이프 라인을 구축해야합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Kafka Connect는 코드 없이 Configuration&lt;/b&gt;&lt;/i&gt;으로 Kafka Cluster에 Data를 Import/Export 할 수 있게 도와줘 반복적인 파이프라인 구축을 간편하게 도와줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;특히 코드를 짤 수 있는 서비스끼리의 통신은 Producer, Consumer를 구현하기 쉽지만, &lt;i&gt;&lt;b&gt;Topic에 생성된 데이터를 DB에서 Produce, Consume&lt;/b&gt;&lt;/i&gt;해야 할 경우 따로 서비스를 만들어야하기 때문에 이때 &lt;i&gt;&lt;b&gt;Connect 를 활용해 별도의 서비스 구현 없이 DB에서 Produce, Consume&lt;/b&gt;&lt;/i&gt;할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsz8I6/btsIHdoNN0H/V3o5qwoJ5cJ9hDC4uEp2c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsz8I6/btsIHdoNN0H/V3o5qwoJ5cJ9hDC4uEp2c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsz8I6/btsIHdoNN0H/V3o5qwoJ5cJ9hDC4uEp2c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdsz8I6%2FbtsIHdoNN0H%2FV3o5qwoJ5cJ9hDC4uEp2c1%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;973&quot; height=&quot;175&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Standalone(단일)/Distribution(분산) mode 지원&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;RestfulAPI&lt;/b&gt; 지원&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Stream 또는 Batch 형태로 데이터 전송 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;커스텀 Connector&lt;/b&gt;를 통한 다양한 plugin 제공(File, S3, Hive, MySQL...)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka &amp;amp; Kafka Connect 활용이 필요할 때&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MSA 아키텍처로 서비스를 구성할 때 동일한 서비스를 스케일 아웃을 통해 부하를 분담할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때의 문제점에 대해 생각해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FP0f4/btsIHGxwJY0/vOmzN2HgOAg3iTRv2a7GP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FP0f4/btsIHGxwJY0/vOmzN2HgOAg3iTRv2a7GP0/img.png&quot; data-alt=&quot;Order Service 1에 주문 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FP0f4/btsIHGxwJY0/vOmzN2HgOAg3iTRv2a7GP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFP0f4%2FbtsIHGxwJY0%2FvOmzN2HgOAg3iTRv2a7GP0%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;939&quot; height=&quot;450&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Order Service 1에 주문 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;APIGateway는 등록된 두개의 orderservice 중에 하나에 API를 넘겨주는데 order service 1에 주문 등록이 되었다고 가정하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;text-align: center; caret-color: transparent; letter-spacing: 0px;&quot; src=&quot;https://blog.kakaocdn.net/dn/bdKnEA/btsIHnY9Uzb/9snQ6YeN7K4fyvmyEAVXp1/img.png&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;433&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 GET을 통해 주문목록을 조회하려고 할때 해당 API가 order service 2에 넘겨준다면 DB가 달라서 주문이 없다고 조회될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터베이스 동기화의 해결법은 세가지가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C4yLe/btsIHCV9BVz/UYu3KNGjjH10kwF9A36oD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C4yLe/btsIHCV9BVz/UYu3KNGjjH10kwF9A36oD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C4yLe/btsIHCV9BVz/UYu3KNGjjH10kwF9A36oD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC4yLe%2FbtsIHCV9BVz%2FUYu3KNGjjH10kwF9A36oD0%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;577&quot; height=&quot;416&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;첫번째로 N개의 서비스에서 한개의 DB를 사용하는 것입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 단에서 부하가 커지고 트랜잭션 관리를 통한 동시성 제어가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mPnwN/btsIH1agGAv/2MsRVMX744AUf7iKGyb9u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mPnwN/btsIH1agGAv/2MsRVMX744AUf7iKGyb9u0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mPnwN/btsIH1agGAv/2MsRVMX744AUf7iKGyb9u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmPnwN%2FbtsIH1agGAv%2F2MsRVMX744AUf7iKGyb9u0%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;669&quot; height=&quot;394&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;두번째는 N개의 서비스의 DB를 메세지, 이벤트 큐잉 시스템을 통해 동기화 시켜주는 것입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Producer와 Consumer 파이프라인을 양방향으로 구축해줘야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crmQP8/btsIGYljw26/9HSvUFcXXkyQ2HJZ1cSQr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crmQP8/btsIGYljw26/9HSvUFcXXkyQ2HJZ1cSQr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crmQP8/btsIGYljw26/9HSvUFcXXkyQ2HJZ1cSQr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrmQP8%2FbtsIGYljw26%2F9HSvUFcXXkyQ2HJZ1cSQr1%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;689&quot; height=&quot;402&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;세번째는 단일 DB 앞단에 메세지 큐잉 시스템을 활용해 동시성에 대한 문제는 큐잉 시스템에 위임해 처리할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추후에 두번째, 세번째 방법을 활용하기 위해 Kafka와 Kafka Connect를 활용해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka &amp;amp; Kafka Connect 환경 설정&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Windows 환경&lt;/b&gt;에서 kafka, kafka Connect, SpringBoot + &lt;b&gt;gradle&lt;/b&gt;, MariaDB를 활용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka 환경설정에 대한 이해를 키우기 위해 로컬로 진행하고, MariaDB는 Docker를 활용해서 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;kafka 다운로드&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://kafka.apache.org/downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kafka.apache.org/downloads&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1721538075856&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;Apache Kafka&quot; data-og-description=&quot;Apache Kafka: A Distributed Streaming Platform.&quot; data-og-host=&quot;kafka.apache.org&quot; data-og-source-url=&quot;https://kafka.apache.org/downloads&quot; data-og-url=&quot;https://kafka.apache.org/downloads&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kxvEm/hyWCJNBXUV/jEkP66nBde7et1rrZrKCrk/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200&quot;&gt;&lt;a href=&quot;https://kafka.apache.org/downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kafka.apache.org/downloads&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kxvEm/hyWCJNBXUV/jEkP66nBde7et1rrZrKCrk/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200');&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;Apache Kafka&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Apache Kafka: A Distributed Streaming Platform.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kafka.apache.org&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;kk.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHIH8B/btsIGr2BI3l/Kn7lc8iT5qhLfVBidXp9H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHIH8B/btsIGr2BI3l/Kn7lc8iT5qhLfVBidXp9H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHIH8B/btsIGr2BI3l/Kn7lc8iT5qhLfVBidXp9H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHIH8B%2FbtsIGr2BI3l%2FKn7lc8iT5qhLfVBidXp9H1%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;1293&quot; height=&quot;533&quot; data-filename=&quot;kk.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1721539524561&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tar xvf kafka_2.12-3.4.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;kafka connect 다운로드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721538013267&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -O http://packages.confluent.io/archive/7.3.1/confluent-community-7.3.1.tar.gz

tar xvf confluent-community-7.3.1.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect JDBC 다운로드&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.confluent.io/hub/confluentinc/kafka-connect-jdbc&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.confluent.io/hub/confluentinc/kafka-connect-jdbc&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721538288681&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;JDBC Connector (Source and Sink)&quot; data-og-description=&quot;Discover 200+ expert-built Apache Kafka connectors for seamless, real-time data streaming and integration. Connect with MongoDB, AWS S3, Snowflake, and more.&quot; data-og-host=&quot;www.confluent.io&quot; data-og-source-url=&quot;https://www.confluent.io/hub/confluentinc/kafka-connect-jdbc&quot; data-og-url=&quot;https://www.confluent.io/hub/confluentinc/kafka-connect-jdbc/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.confluent.io/hub/confluentinc/kafka-connect-jdbc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.confluent.io/hub/confluentinc/kafka-connect-jdbc&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;JDBC Connector (Source and Sink)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Discover 200+ expert-built Apache Kafka connectors for seamless, real-time data streaming and integration. Connect with MongoDB, AWS S3, Snowflake, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.confluent.io&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka 폴더(work) 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;압훅해제한 Kafka, KafaConnect(confluetn-7.3.1), KafkaConnect-JDBC를 work 폴더를 만들어 모으겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4kMcu/btsIHffPOki/UlEJPLLJLoHeb59kdjN250/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4kMcu/btsIHffPOki/UlEJPLLJLoHeb59kdjN250/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4kMcu/btsIHffPOki/UlEJPLLJLoHeb59kdjN250/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4kMcu%2FbtsIHffPOki%2FUlEJPLLJLoHeb59kdjN250%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;785&quot; height=&quot;301&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;301&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect JDBC Plugin 추가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; confluent-7.3.1/etc/kafka/connect-distributed.properties PlugIn 변경 &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_connect.png&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP4Hbg/btsIGWVjviW/3SrlSkRHnpDMWPsBy5FB1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP4Hbg/btsIGWVjviW/3SrlSkRHnpDMWPsBy5FB1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP4Hbg/btsIGWVjviW/3SrlSkRHnpDMWPsBy5FB1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP4Hbg%2FbtsIGWVjviW%2F3SrlSkRHnpDMWPsBy5FB1K%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;761&quot; height=&quot;180&quot; data-filename=&quot;edited_connect.png&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;180&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect MariaDB Connect 의존성 추가&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SpringBoot 프로젝트에서 Gradle을 통해 MariaDB Java Client 의존성을 추가하고, 해당 라이브러리를 복사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;windows + gradle 기준 confluent-7.3.1\share\java\kafka 에 gradle로 추가한 mariadb java client 의존성 복사 &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721538634006&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Users\PC\.gradle\caches\modules-2\files-2.1\org.mariadb.jdbc\mariadb-java-client\${version}\${cache}\mariadb-java-client-${version}&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_maria.png&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DozdE/btsIGFTSxnk/AMfCjTtJ78dzTR80aOh1UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DozdE/btsIGFTSxnk/AMfCjTtJ78dzTR80aOh1UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DozdE/btsIGFTSxnk/AMfCjTtJ78dzTR80aOh1UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDozdE%2FbtsIGFTSxnk%2FAMfCjTtJ78dzTR80aOh1UK%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;1302&quot; height=&quot;136&quot; data-filename=&quot;edited_maria.png&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1721538667290&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;confluent-7.3.1\share\java\kafka&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_mar.png&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/et51EV/btsIHHJRPzI/OzvWTkQF8M5eK2q9aMuWA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/et51EV/btsIHHJRPzI/OzvWTkQF8M5eK2q9aMuWA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/et51EV/btsIHHJRPzI/OzvWTkQF8M5eK2q9aMuWA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fet51EV%2FbtsIHHJRPzI%2FOzvWTkQF8M5eK2q9aMuWA0%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;780&quot; height=&quot;121&quot; data-filename=&quot;edited_mar.png&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka &amp;amp; Kafka Connect 실행&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MariaDB 실행&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도커를 활용해 MariaDB를 실행합니다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721539475904&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --name mariadb -d -p 3306:3306 --restart=always -e MYSQL_ROOT_PASSWORD=1q2w3e4r mariadb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Zookeeper 실행&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카프카 클러스터를 관리해줄 주키퍼를 먼저 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721539198620&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd work/kafka_2.12-3.4.0
./bin/windows/zookeeper-server-start.bat ./config/zookeeper.properties # windows 기준&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;744&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TVjzz/btsIF1I4hZo/Hz9pUQo6462zbtKgnx5Zjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TVjzz/btsIF1I4hZo/Hz9pUQo6462zbtKgnx5Zjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TVjzz/btsIF1I4hZo/Hz9pUQo6462zbtKgnx5Zjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTVjzz%2FbtsIF1I4hZo%2FHz9pUQo6462zbtKgnx5Zjk%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;744&quot; height=&quot;76&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Kafka Server 실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721538990021&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;./bin/windows/kafka-server-start.bat ./config/server.properties&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;972&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/80j3M/btsIG8mLtbA/AUf06otk9gPad2ggAiSY30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/80j3M/btsIG8mLtbA/AUf06otk9gPad2ggAiSY30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/80j3M/btsIG8mLtbA/AUf06otk9gPad2ggAiSY30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F80j3M%2FbtsIG8mLtbA%2FAUf06otk9gPad2ggAiSY30%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;972&quot; height=&quot;66&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;66&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect 실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721539242517&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd work/confluent-7.3.1
./bin/windows/connect-distributed.bat ./etc/kafka/connect-distributed.properties #windows 기준&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect&amp;nbsp; Source&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721537916664&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST http://localhost:8083/connectors

{
    &quot;name&quot;: &quot;my-source-connect&quot;, # 등록할 Connector 이름
    &quot;config&quot;: {
        &quot;connector.class&quot;: &quot;io.confluent.connect.jdbc.JdbcSourceConnector&quot;,    # source connector 종류
        &quot;connection.url&quot;: &quot;jdbc:mariadb://localhost:3306/mydb&quot;, 
        &quot;connection.user&quot;: &quot;root&quot;,
        &quot;connection.password&quot;: &quot;1q2w3e4r&quot;,
        &quot;mode&quot;: &quot;incrementing&quot;, # 데이터 추가 시 데이터 polling 방식
        &quot;incrementing.column.name&quot;: &quot;id&quot;, # 자동증가 column
        &quot;table.whitelist&quot;: &quot;mydb.users&quot;, # 데이터 변경 감지 table 명, 여러 db에 동일한 테이블이 존재하면 db.table로 명시
        &quot;topic.prefix&quot;: &quot;my_topic_&quot;, # kafka 토픽에 저장될 형식
        &quot;tasks.max&quot;: &quot;1&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CVzfn/btsIGmf1Wl1/UXk84MVKluEKt26N97POP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CVzfn/btsIGmf1Wl1/UXk84MVKluEKt26N97POP0/img.png&quot; data-alt=&quot;whitelist database.tables 명시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CVzfn/btsIGmf1Wl1/UXk84MVKluEKt26N97POP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCVzfn%2FbtsIGmf1Wl1%2FUXk84MVKluEKt26N97POP0%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;1198&quot; height=&quot;756&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;whitelist database.tables 명시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조회도 등록과 마찬가지로 RestfulAPI를 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OXrpN/btsIG0XyKDx/sPbeKsHbeZHeKM9hQgIndK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OXrpN/btsIG0XyKDx/sPbeKsHbeZHeKM9hQgIndK/img.png&quot; data-alt=&quot;등록된 Connectors 조회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OXrpN/btsIG0XyKDx/sPbeKsHbeZHeKM9hQgIndK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOXrpN%2FbtsIG0XyKDx%2FsPbeKsHbeZHeKM9hQgIndK%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;1190&quot; height=&quot;463&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;등록된 Connectors 조회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tjkqc/btsIHnLDWKc/YP8xpjXUHg5atW2GKNs2Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tjkqc/btsIHnLDWKc/YP8xpjXUHg5atW2GKNs2Y1/img.png&quot; data-alt=&quot;상세 조회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tjkqc/btsIHnLDWKc/YP8xpjXUHg5atW2GKNs2Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftjkqc%2FbtsIHnLDWKc%2FYP8xpjXUHg5atW2GKNs2Y1%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;652&quot; height=&quot;571&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상세 조회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Connectors 등록후에는 이전에 없던 connect 관련 토픽들을 조회할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EhhbW/btsIHG5kzDA/UFyoc0iGFnLLuL6CpYfJkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EhhbW/btsIHG5kzDA/UFyoc0iGFnLLuL6CpYfJkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EhhbW/btsIHG5kzDA/UFyoc0iGFnLLuL6CpYfJkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEhhbW%2FbtsIHG5kzDA%2FUFyoc0iGFnLLuL6CpYfJkK%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;702&quot; height=&quot;130&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Source 연결 테이블 INSERT&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Soruce Connect와 연결된 DB에서 Insert를 실행하면 토픽을 통해 데이터 생성되는 것을 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721536480005&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;insert into users(user_id, pwd, name) values ('users1', 'test1111', 'user name');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdZr7a/btsIHGRMk6W/j8MfgAjrSZRLSsxs9JvJU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdZr7a/btsIHGRMk6W/j8MfgAjrSZRLSsxs9JvJU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdZr7a/btsIHGRMk6W/j8MfgAjrSZRLSsxs9JvJU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdZr7a%2FbtsIHGRMk6W%2Fj8MfgAjrSZRLSsxs9JvJU1%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;867&quot; height=&quot;277&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_mytopic.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PaBqk/btsIIiplL78/Jf8E1I8cUARBTFFPkn5bpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PaBqk/btsIIiplL78/Jf8E1I8cUARBTFFPkn5bpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PaBqk/btsIIiplL78/Jf8E1I8cUARBTFFPkn5bpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPaBqk%2FbtsIIiplL78%2FJf8E1I8cUARBTFFPkn5bpK%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;681&quot; height=&quot;116&quot; data-filename=&quot;edited_mytopic.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;116&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;pre id=&quot;code_1721536653587&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./bin/windows/kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic my_topic_users --from-beginning&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;723&quot; data-origin-height=&quot;181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi80Rz/btsIGFGi2Nq/S0PVneYVT3a3FgNUcn9L6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi80Rz/btsIGFGi2Nq/S0PVneYVT3a3FgNUcn9L6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi80Rz/btsIGFGi2Nq/S0PVneYVT3a3FgNUcn9L6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi80Rz%2FbtsIGFGi2Nq%2FS0PVneYVT3a3FgNUcn9L6k%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;723&quot; height=&quot;181&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;181&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;pre id=&quot;code_1721536868743&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;schema&quot;: {
    &quot;type&quot;: &quot;struct&quot;,
    &quot;fields&quot;: [
      {
        &quot;type&quot;: &quot;int32&quot;,
        &quot;optional&quot;: false,
        &quot;field&quot;: &quot;id&quot;
      },
      {
        &quot;type&quot;: &quot;string&quot;,
        &quot;optional&quot;: true,
        &quot;field&quot;: &quot;user_id&quot;
      },
      {
        &quot;type&quot;: &quot;string&quot;,
        &quot;optional&quot;: true,
        &quot;field&quot;: &quot;pwd&quot;
      },
      {
        &quot;type&quot;: &quot;string&quot;,
        &quot;optional&quot;: true,
        &quot;field&quot;: &quot;name&quot;
      },
      {
        &quot;type&quot;: &quot;int64&quot;,
        &quot;optional&quot;: true,
        &quot;name&quot;: &quot;org.apache.kafka.connect.data.Timestamp&quot;,
        &quot;version&quot;: 1,
        &quot;field&quot;: &quot;created_at&quot;
      }
    ],
    &quot;optional&quot;: false,
    &quot;name&quot;: &quot;users&quot;
  },
  &quot;payload&quot;: {
    &quot;id&quot;: 1,
    &quot;user_id&quot;: &quot;users1&quot;,
    &quot;pwd&quot;: &quot;test1111&quot;,
    &quot;name&quot;: &quot;user name&quot;,
    &quot;created_at&quot;: 1721536482000
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Kafka Connect&amp;nbsp; Sink&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생성된 데이터를 Consume 하기 위해 Sink Connect를 등록합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721537635129&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST http://localhost:8083/connectors

{
    &quot;name&quot;: &quot;my-sink-connect&quot;,
    &quot;config&quot;: {
        &quot;connector.class&quot;: &quot;io.confluent.connect.jdbc.JdbcSinkConnector&quot;,
        &quot;connection.url&quot;: &quot;jdbc:mariadb//localhost:3306/mydb&quot;,
        &quot;connection.user&quot;: &quot;root&quot;,
        &quot;connection.password&quot;: &quot;1q2w3e4r&quot;,
        &quot;auto.create&quot;: &quot;true&quot;, # 데이터 넣은 테이블 없으면 생성
        &quot;auto.evolve&quot;: &quot;true&quot;, # 특정 데이터 colmun 누락 시 alter로 테이블 변경
        &quot;delete.enabled&quot;: &quot;false&quot;, # null 레코드 값 삭제 여부
        &quot;tasks.max&quot;: &quot;1&quot;,
        &quot;topics&quot;: &quot;my_topic_users&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ER1WE/btsIGrhbGlI/DKOeADYPbdsEo3jQu4VN20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ER1WE/btsIGrhbGlI/DKOeADYPbdsEo3jQu4VN20/img.png&quot; data-alt=&quot;sink connect 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ER1WE/btsIGrhbGlI/DKOeADYPbdsEo3jQu4VN20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FER1WE%2FbtsIGrhbGlI%2FDKOeADYPbdsEo3jQu4VN20%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;872&quot; height=&quot;773&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sink connect 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzwc9q/btsIHzkN5b4/dKBxzhs8HHN56crcSmsKyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzwc9q/btsIHzkN5b4/dKBxzhs8HHN56crcSmsKyK/img.png&quot; data-alt=&quot;connects 조회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzwc9q/btsIHzkN5b4/dKBxzhs8HHN56crcSmsKyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbzwc9q%2FbtsIHzkN5b4%2FdKBxzhs8HHN56crcSmsKyK%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;512&quot; height=&quot;356&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;connects 조회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSuB9o/btsIHnESIDq/lW4Z2uygKS5vriaZK8nyS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSuB9o/btsIHnESIDq/lW4Z2uygKS5vriaZK8nyS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSuB9o/btsIHnESIDq/lW4Z2uygKS5vriaZK8nyS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSuB9o%2FbtsIHnESIDq%2FlW4Z2uygKS5vriaZK8nyS0%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;730&quot; height=&quot;584&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Source 연결 테이블 INSERT&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다시 source와 연결된 테이블에 데이터를 produce하면 Sink를 통해 데이터를 consume해 데이터가 복사됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721537849956&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;insert into users(user_id, pwd, name) values ('users2', 'test33', 'user name1');
insert into users(user_id, pwd, name) values ('users3', 'test333', 'user name3');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz5PJK/btsIIzdkipR/eeePpD7n3Ow64Qd0kedhmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz5PJK/btsIIzdkipR/eeePpD7n3Ow64Qd0kedhmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz5PJK/btsIIzdkipR/eeePpD7n3Ow64Qd0kedhmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz5PJK%2FbtsIIzdkipR%2FeeePpD7n3Ow64Qd0kedhmK%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;314&quot; height=&quot;190&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;190&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;845&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1ehJT/btsIG3fCqCf/mAm14HB2N8L89YPhHrH4Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1ehJT/btsIG3fCqCf/mAm14HB2N8L89YPhHrH4Z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1ehJT/btsIG3fCqCf/mAm14HB2N8L89YPhHrH4Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1ehJT%2FbtsIG3fCqCf%2FmAm14HB2N8L89YPhHrH4Z0%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;845&quot; height=&quot;72&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Table 조회&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721537830946&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from users;&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;479&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csCyn0/btsIG9mww8a/yuPuGOtTPPFLTLtJSknpfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csCyn0/btsIG9mww8a/yuPuGOtTPPFLTLtJSknpfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csCyn0/btsIG9mww8a/yuPuGOtTPPFLTLtJSknpfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsCyn0%2FbtsIG9mww8a%2FyuPuGOtTPPFLTLtJSknpfK%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;479&quot; height=&quot;214&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Sink Table 조회&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721537869728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from my_topic_users;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KImfK/btsIGxhfcg2/3kGQ9EzLCJEZ0cc6NECKXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KImfK/btsIGxhfcg2/3kGQ9EzLCJEZ0cc6NECKXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KImfK/btsIGxhfcg2/3kGQ9EzLCJEZ0cc6NECKXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKImfK%2FbtsIGxhfcg2%2F3kGQ9EzLCJEZ0cc6NECKXK%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;454&quot; height=&quot;95&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음 포스팅&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;세번째 방법에 대한 포스팅입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/195?category=1028453&quot;&gt;https://anythingis.tistory.com/195?category=1028453&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721716816134&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;[MSA] 스프링에서 INSERT Query Kafka에 위임하기&quot; data-og-description=&quot;개요https://anythingis.tistory.com/193 &amp;nbsp;[MSA] Apache Kafka &amp;amp; Kafka ConnectKafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/195?category=1028453&quot; data-og-url=&quot;https://anythingis.tistory.com/195&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uJwD5/hyWCCawj0y/c0PPJHarXR8wKpbRbKKkKk/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/QUNVD/hyWCMRKVRn/rvCkuyQHpmYXfAQv8nFvQ1/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/KR3l8/hyWCKl6Awq/XpMJRyAyuFifNx1kZm4R21/img.png?width=1525&amp;amp;height=533&amp;amp;face=0_0_1525_533&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/195?category=1028453&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/195?category=1028453&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uJwD5/hyWCCawj0y/c0PPJHarXR8wKpbRbKKkKk/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/QUNVD/hyWCMRKVRn/rvCkuyQHpmYXfAQv8nFvQ1/img.png?width=689&amp;amp;height=402&amp;amp;face=75_53_140_313,https://scrap.kakaocdn.net/dn/KR3l8/hyWCKl6Awq/XpMJRyAyuFifNx1kZm4R21/img.png?width=1525&amp;amp;height=533&amp;amp;face=0_0_1525_533');&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;[MSA] 스프링에서 INSERT Query Kafka에 위임하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요https://anythingis.tistory.com/193 &amp;nbsp;[MSA] Apache Kafka &amp;amp; Kafka ConnectKafkaKafka 개요현재 하나의 서비스는 다양한 서비스와 연동됩니다. 이때 end-to-end 방식으로 연결되어 있으면 속도상 빠르다는 장점이 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.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;</description>
      <category>개발로그</category>
      <category>kafka</category>
      <category>kafka connect</category>
      <category>MSA</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/193</guid>
      <comments>https://anythingis.tistory.com/193#entry193comment</comments>
      <pubDate>Sun, 21 Jul 2024 16:29:23 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] Spring Cloud Bus Amqp &amp;amp; rabbitMQ</title>
      <link>https://anythingis.tistory.com/192</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Cloud Bus를 활용하는 이유&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Cloud Config Server - Client로 설정 정보를 변경할 수 있지만, 변경되는 Client의 Spring Boot Actuator Refresh API 호출을 통해 변경된 사항들을 로딩해줘야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_cloud 4.jpg&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U52Bh/btsID5pJv0N/JBnQXiLfQ3kkxmt6HNn9bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U52Bh/btsID5pJv0N/JBnQXiLfQ3kkxmt6HNn9bk/img.png&quot; data-alt=&quot;cloud server 에서 모든 cloud client 주소 관리 필요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U52Bh/btsID5pJv0N/JBnQXiLfQ3kkxmt6HNn9bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU52Bh%2FbtsID5pJv0N%2FJBnQXiLfQ3kkxmt6HNn9bk%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;827&quot; height=&quot;394&quot; data-filename=&quot;edited_cloud 4.jpg&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cloud server 에서 모든 cloud client 주소 관리 필요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;config 1.jpg&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u5C2d/btsIDet2IyK/X9pc1ONPBotuTX8c4KnlF1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u5C2d/btsIDet2IyK/X9pc1ONPBotuTX8c4KnlF1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u5C2d/btsIDet2IyK/X9pc1ONPBotuTX8c4KnlF1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu5C2d%2FbtsIDet2IyK%2FX9pc1ONPBotuTX8c4KnlF1%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;1268&quot; height=&quot;556&quot; data-filename=&quot;config 1.jpg&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;config 2.jpg&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLklYo/btsIBBEbVLm/FzQPHa4rTci6jtxzVrplDK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLklYo/btsIBBEbVLm/FzQPHa4rTci6jtxzVrplDK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLklYo/btsIBBEbVLm/FzQPHa4rTci6jtxzVrplDK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLklYo%2FbtsIBBEbVLm%2FFzQPHa4rTci6jtxzVrplDK%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;1262&quot; height=&quot;621&quot; data-filename=&quot;config 2.jpg&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설정 정보가 변경 되었을 때 모든 마이크로 서비스에 정보를 다시 로딩하라고 요구하는 것은 어떤 서비스가 어떤 설정 정보를 갖고 있는지 체크하는 것이 번거롭기도하고 실수로 중요한 설정을 변경하지 않으면 오류의 확률도 생깁니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cloud 5.jpg&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhWnmZ/btsIDsFGKLb/7vAy4eJGQkUGV0rjAXkqI0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhWnmZ/btsIDsFGKLb/7vAy4eJGQkUGV0rjAXkqI0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhWnmZ/btsIDsFGKLb/7vAy4eJGQkUGV0rjAXkqI0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhWnmZ%2FbtsIDsFGKLb%2F7vAy4eJGQkUGV0rjAXkqI0%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;818&quot; height=&quot;516&quot; data-filename=&quot;cloud 5.jpg&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이럴때 Spring Cloud Bus를 활용하면 변경 내용을 감지하고 자동으로 Cloud Config Client의 Refresh API를 호출할 수 있습니다. 동적으로 설정 정보를 변경하기 위한 Message Queue에 Config Client(설정 정보 연결 노드)들이 연결되어 있어야하고, Cloud Bus에 연결된 마이크로 서비스를 호출하게 되면 다른 서비스들도 설정 정보가 변경됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Rabbit MQ(Docker 기반)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #000000; text-align: start;&quot;&gt; 메시지 지향 미들웨어를 위한 개방형 표준 응용 프로토콜인 AMQP를 활용하는 애플리케이션입니다.&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도커로는 설치와 실행, 백그라운드 실행이 매우 쉽습니다. 이왕이면 도커로...&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721721665337&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# windows 기준, 줄바꿈 명렁어 (^), linux 기준 (\)
docker run -d --name rabbitmq --restart always ^
 -p 15672:15672 -p 5672:5672 -p 15671:15671 -p 5671:5671 -p 4369:4369 ^
 -e RABBITMQ_DEFAULT_USER=guest ^
 -e RABBITMQ_DEFAULT_PASS=guest rabbitmq:management
# 5672 -&amp;gt; rabbitMq, 15672 -&amp;gt; admin web page, 4369 -&amp;gt; EMPD, 5671 -&amp;gt; TLS&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Rabbit MQ(Windows 기반)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Erlang은 RabbitMQ의 핵심적인 실행 환경을 제공하므로, RabbitMQ를 설치할 때 Erlang/OTP가 필수적입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;윈도우는 rabbitMq와 Erlang/OTP 패키지가 분리되어 있어 사전 설치가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Erlang 설치&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.erlang.org/downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.erlang.org/downloads&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Erlang &lt;span style=&quot;color: #000000;&quot;&gt;설치후 환경변수 설정&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;erlang.png&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;767&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SLPx4/btsICVuPZTN/MLmvYzgiLsoRzL5yw4L1gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SLPx4/btsICVuPZTN/MLmvYzgiLsoRzL5yw4L1gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SLPx4/btsICVuPZTN/MLmvYzgiLsoRzL5yw4L1gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSLPx4%2FbtsICVuPZTN%2FMLmvYzgiLsoRzL5yw4L1gk%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;811&quot; height=&quot;767&quot; data-filename=&quot;erlang.png&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;767&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;rabbit mq 다운로드&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;a style=&quot;color: #000000;&quot; href=&quot;https://rabbitmq.com/install-windows.html#installer&quot;&gt;https://rabbitmq.com/install-windows.html#installer&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_rabbit.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6QdWo/btsIB7W7nor/JCaplAoWGx0gKl8uGk3RR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6QdWo/btsIB7W7nor/JCaplAoWGx0gKl8uGk3RR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6QdWo/btsIB7W7nor/JCaplAoWGx0gKl8uGk3RR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6QdWo%2FbtsIB7W7nor%2FJCaplAoWGx0gKl8uGk3RR1%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;862&quot; height=&quot;218&quot; data-filename=&quot;edited_rabbit.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;218&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;416&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brXEgx/btsIBIQNcJb/98dEKexrG9e9cRir9WxLlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brXEgx/btsIBIQNcJb/98dEKexrG9e9cRir9WxLlk/img.png&quot; data-alt=&quot;rabbitmq promt 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brXEgx/btsIBIQNcJb/98dEKexrG9e9cRir9WxLlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrXEgx%2FbtsIBIQNcJb%2F98dEKexrG9e9cRir9WxLlk%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;416&quot; height=&quot;217&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;rabbitmq promt 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백그라운드 실행&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721196921626&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rabbitmq-server -detached&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관리자 웹 페이지 설정&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721196954430&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rabbitmq-plugins enable rabbitmq_management&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #3a32c3;&quot;&gt;&lt;b&gt;플러그인 설정하면 http://localhost:15672로 관리자 웹페이지에 접근할 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #3a32c3;&quot;&gt;&lt;b&gt;ID : guest&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #3a32c3;&quot;&gt;&lt;b&gt;PW : guest&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #3a32c3;&quot;&gt;&lt;b&gt;만약 접근이 안되면 서비스 삭제 후 다시 위의 과정을 반복하시면 됩니다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721197049245&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rabbitmq-service.bat remove
rabbitmq-service.bat install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Config Server &amp;amp; Cloud bus&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Config Server &lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존성 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721198469811&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bus-amqp'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721198523657&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  application:
    name: config-service
  rabbitmq: # rabbit mq
    host: 127.0.0.1
    port: 5672 # amqp port
    username: guest
    password: guest

management:
  endpoints:
    web:
      exposure:
        include: health, busrefresh # cloud bus refresh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Config Client&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존성 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721198559467&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bus-amqp'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721198601637&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  application:
    name: user-service 
  rabbitmq: # rabbit mq
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans, busrefresh # cloud bus refresh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 설정을 완료하면 Cloud Config에 등록된 Micro service에 POST actuator/busresfresh API 호출을 하게되면 Cloud Bus에 등록된 다른 서비스에도 설정 변경이 적용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예시&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;User service의 helath check 컨트롤러입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721198780110&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/health_check&quot;)
    public String status(){
        return String.format(&quot;It's Working in User Service on PORT %s, token secret : %s, token time : %s&quot; ,
                environment.getProperty(&quot;local.server.port&quot;), environment.getProperty(&quot;token.secret&quot;),
                environment.getProperty(&quot;token.expiration_time&quot;));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kYoNX/btsICwB8ZgS/L7X4f4uN4iTHWQrOe7qBW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kYoNX/btsICwB8ZgS/L7X4f4uN4iTHWQrOe7qBW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kYoNX/btsICwB8ZgS/L7X4f4uN4iTHWQrOe7qBW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkYoNX%2FbtsICwB8ZgS%2FL7X4f4uN4iTHWQrOe7qBW1%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;1166&quot; height=&quot;541&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;541&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cloud Config 에서 관리하는 application 변경&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1721198720025&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;token:
  expiration_time: 86400000
  secret: BusBuscD08xCg+eiWnntQyYKCF/ZE7h9FbF5lN8DsNCZJVGN1IsXREyEmI/o4fEpqRYqL6eIQfFE/h6RFaQ4vQALY84Q==

gateway:
  ip: 192.168.0.71&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Git Commit &amp;amp; Push&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1721198745263&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git add ecommerce.yml
git commit -m &quot;feat: change to use cloud bus&quot;
git push&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;API Gateway 서비스에 Bus Refresh&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cloud Client인 API Gateway 서비스에 bus Refresh API를 호출해도 User service에서 설정이 변경하는지 확인하기 위해 API Gateway 서비스에 호출합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNZOrD/btsICdwesv7/v8Ordooc3ouzN5u7vyMbmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNZOrD/btsICdwesv7/v8Ordooc3ouzN5u7vyMbmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNZOrD/btsICdwesv7/v8Ordooc3ouzN5u7vyMbmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNZOrD%2FbtsICdwesv7%2Fv8Ordooc3ouzN5u7vyMbmk%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;1188&quot; height=&quot;546&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;546&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;1179&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/se27X/btsICwoB6q1/NgYB3Bbe8ZRpoCWJCFVZQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/se27X/btsICwoB6q1/NgYB3Bbe8ZRpoCWJCFVZQ1/img.png&quot; data-alt=&quot;동일한 토큰 이용 But AuthorizationFilter의 Token이 변경되어 401 코드 반환&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/se27X/btsICwoB6q1/NgYB3Bbe8ZRpoCWJCFVZQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fse27X%2FbtsICwoB6q1%2FNgYB3Bbe8ZRpoCWJCFVZQ1%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;1179&quot; height=&quot;543&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동일한 토큰 이용 But AuthorizationFilter의 Token이 변경되어 401 코드 반환&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;API Gateway에 bus refresh 호출시에 cloud bus를 통해 user service에도 refresh가 호출되어 토큰이 변경되어 접근되지 않습니다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재로그인을 통해 변경된 토큰 키로 만든 토큰으로 health check &lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;busbus.png&quot; data-origin-width=&quot;1187&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCa8UP/btsIBLtgHcM/kYReVo8pNDWHbhYcKPFT5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCa8UP/btsIBLtgHcM/kYReVo8pNDWHbhYcKPFT5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCa8UP/btsIBLtgHcM/kYReVo8pNDWHbhYcKPFT5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCa8UP%2FbtsIBLtgHcM%2FkYReVo8pNDWHbhYcKPFT5k%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;1187&quot; height=&quot;554&quot; data-filename=&quot;busbus.png&quot; data-origin-width=&quot;1187&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;흐름&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. Cloud Config Server 관리하는 설정 정보 변경(깃, 로컬파일)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. Cloud Bus에 연결된 Micro Service에 POST /acutator/busrefresh API 호출&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. Cloud Bus에 연결된 다른 Micro Service에도 refresh 호출되어 설정 정보 변경&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Config 정보를 관리하는 서버, Config 정보를 받아보는 클라이언트로 나뉘고, 클라이언트는 변경된 Config 정보 로딩을 위한 Spring Boot Actuator Refesh, Cloud Config 설정 정보 자동 동기화를 위한 Cloud Bus까지, MSA 세계는 너무 방대한 것 같습니다;; 많은 기술을 활용하는 만큼 기술의 목적을 잊지 않기 위해 꾸준히 정리하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발로그</category>
      <category>MSA</category>
      <category>rabbitmq</category>
      <category>spring cloud bus</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/192</guid>
      <comments>https://anythingis.tistory.com/192#entry192comment</comments>
      <pubDate>Wed, 17 Jul 2024 16:02:15 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] Spring Cloud Config &amp;amp; Spring Boot Actuator</title>
      <link>https://anythingis.tistory.com/191</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cloud Config Server 활용의 이유 &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보통 스프링 내에 application 설정파일을 변경하고 재빌드를 통해 설정을 적용합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 도메인별로 흩어진 마이크로 서비스 구조에서는 이런 관리를 할 수 있는 Cloud Config Server로 관리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; Local Git Repository의 정보에 접근하는 Config Server를 만듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그림으로 먼저 설명하자면 이런 느낌입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;clound config.jpg&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ9MCw/btsIBJgFeNS/ApF323ly79QHv7kMvgpS9k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ9MCw/btsIBJgFeNS/ApF323ly79QHv7kMvgpS9k/img.jpg&quot; data-alt=&quot;요런 느낌&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ9MCw/btsIBJgFeNS/ApF323ly79QHv7kMvgpS9k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ9MCw%2FbtsIBJgFeNS%2FApF323ly79QHv7kMvgpS9k%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;707&quot; height=&quot;356&quot; data-filename=&quot;clound config.jpg&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요런 느낌&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부에서 적용할 yml 파일 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ecommerce.yml 생성&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721110423961&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;token:
  expiration_time: 1
  secret: cD08xCg+eiWnntQyYKCF/ZE7h9FbF5lN8D

gateway:
  ip: 192.168.0.71&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;git 등록&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;local git에 commit 된 파일에 Cloud Server가 접근할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721110540028&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git init
git add ecommerce.yml
git commit -m &quot;add local yml&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cloud Config Server 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b95AAb/btsIzMy6TEd/gzBKXFVyQtFMlkGJ39nGhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b95AAb/btsIzMy6TEd/gzBKXFVyQtFMlkGJ39nGhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b95AAb/btsIzMy6TEd/gzBKXFVyQtFMlkGJ39nGhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb95AAb%2FbtsIzMy6TEd%2FgzBKXFVyQtFMlkGJ39nGhK%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;982&quot; height=&quot;781&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;781&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;964&quot; data-origin-height=&quot;779&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJnT45/btsICcQlG4L/BX0dqNQ4gFk6k5V0dwb58K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJnT45/btsICcQlG4L/BX0dqNQ4gFk6k5V0dwb58K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJnT45/btsICcQlG4L/BX0dqNQ4gFk6k5V0dwb58K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJnT45%2FbtsICcQlG4L%2FBX0dqNQ4gFk6k5V0dwb58K%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;964&quot; height=&quot;779&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;779&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@EnableConfigServer 추가&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721110667158&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableConfigServer
public class ConfigServiceApplication {

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

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml&amp;nbsp; 설정 추가&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721110732188&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 8888

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: file://E:\work # ecommerce.yml 파일 있는 디렉토리 위치&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;http://localhost:8888/ecommerce/default&lt;/span&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;836&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9o13O/btsIBglHPAw/Q0LOoboycR8dkSfkwyIKjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9o13O/btsIBglHPAw/Q0LOoboycR8dkSfkwyIKjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9o13O/btsIBglHPAw/Q0LOoboycR8dkSfkwyIKjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9o13O%2FbtsIBglHPAw%2FQ0LOoboycR8dkSfkwyIKjk%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;836&quot; height=&quot;457&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;457&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cloud Config Server - Client 연결&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Client는 MSA 구조 내에 Config Server에서 설정 정보를 제공받을 서비스입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;clound cofnig + user service.jpg&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwL4L9/btsIAbFC8Xf/UrKsNdVt0RdcXcprDlV37K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwL4L9/btsIAbFC8Xf/UrKsNdVt0RdcXcprDlV37K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwL4L9/btsIAbFC8Xf/UrKsNdVt0RdcXcprDlV37K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwL4L9%2FbtsIAbFC8Xf%2FUrKsNdVt0RdcXcprDlV37K%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;1391&quot; height=&quot;363&quot; data-filename=&quot;clound cofnig + user service.jpg&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;bootstrap.yml은 Spring Cloud Config 파일입니다. Spring Cloud 에서 지원하는 discovery, gateway 등의 설정값은 해당 파일에서 설정해야합니다. &lt;i&gt;&lt;b&gt;bootstrap은 application 설정 적용보다 우선순위가 높아 먼저 적용됩니다. &lt;/b&gt;&lt;/i&gt;레거시한 방법이라 지원을 안한다고 하는데 다시 바뀌었는지 지금 되기도 하고 버젼 업데이트도 꾸준합니다...?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;config without boot.jpg&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tPLAS/btsIASrXCYg/fAuQzSls0IoDZu8LABbui0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tPLAS/btsIASrXCYg/fAuQzSls0IoDZu8LABbui0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tPLAS/btsIASrXCYg/fAuQzSls0IoDZu8LABbui0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtPLAS%2FbtsIASrXCYg%2FfAuQzSls0IoDZu8LABbui0%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;1206&quot; height=&quot;347&quot; data-filename=&quot;config without boot.jpg&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Spring Boot 2.4 이상부터는 application.yml에서 bootstrap 관련 설정도 할 수 있습니다.&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;bootstrap 활용 방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;cloud client service 의존성 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721112055980&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-config'
    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bootstrap'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;resources 폴더 내에 bootstrap.yml 생성&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721112128794&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: ecommerce&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml 활용방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; cloud client service 의존성 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721112412891&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-config'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml 내에 설정 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721112193516&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  cloud:
    config:
      name: ecommerce # 외부에서 적용할 yml 파일
  config:
    import: optional:configserver:http://localhost:8888 # cloud config server 주소&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 방법 둘 중 하나를 골라 설정을 적용하면, 해당 서비스의 application.yml에는 없지만 ecommerce.yml에 있는 정보도 Environment를 통해 접근이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Boot Actuator&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 변경된 ecommerce.yml이 커밋되면 Cloud Server는 변경된 정보를 제공하지만, Cloud Client는 서비스를 다시 기동해서 빌드를 통해 변경해야합니다. &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;혼자 개발하는 환경에서는 문제가 없을 수 있으나 팀으로 진행하는 상황에서 팀원이 특정 서비스를 활용하거나, 운영 환경에서는 함부로 서버를 내리지 못합니다. &lt;i&gt;&lt;b&gt;서비스를 재가동 시키지 않고 유연하게 적용&lt;/b&gt;&lt;/i&gt;하기 위해서 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Boot Actuator를 통해 외부에서 API 호출을 통해 &lt;i&gt;&lt;b&gt;애플리케이션 컨텍스트 내에서 변경된 설정을 다시 로드&lt;/b&gt;&lt;/i&gt;하고 적용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존성 추가&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721113176572&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '3.3.1'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;application.yml 노출할 actuator 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1721113655250&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans # 컨텍스트 로드, 헬스체크, 컨텍스트 등록된 빈 조회&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Security 필터 actuator/**&amp;nbsp; 하위 인가 해제&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1721113262275&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurity {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);

        http.authorizeHttpRequests((request) -&amp;gt;  request
                .requestMatchers(antMatcher(&quot;/actuator/**&quot;)).permitAll()
                .requestMatchers(antMatcher(&quot;/h2-console/**&quot;)).permitAll()
        ).sessionManagement((session) -&amp;gt; session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

       
        http.headers(header -&amp;gt; header.frameOptions(
                frameOptionsConfig -&amp;gt; frameOptionsConfig.disable()));


        return http.build();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Actuator 이용한 설정 변경 적용&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cloud config + user service + actuator.jpg&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vw3lp/btsIBAqHH6j/8UwkQmYS0XJQESRb9GrzK0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vw3lp/btsIBAqHH6j/8UwkQmYS0XJQESRb9GrzK0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vw3lp/btsIBAqHH6j/8UwkQmYS0XJQESRb9GrzK0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvw3lp%2FbtsIBAqHH6j%2F8UwkQmYS0XJQESRb9GrzK0%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;1565&quot; height=&quot;467&quot; data-filename=&quot;cloud config + user service + actuator.jpg&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1721113490763&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. ecommerce.yml 변경
2. git add , commit 진행
3. cloud client service 주소에 POST actuator/refresh API 호출
4. cloud client 변경 설정 적용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그</category>
      <category>actuator</category>
      <category>config</category>
      <category>spring cloud</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/191</guid>
      <comments>https://anythingis.tistory.com/191#entry191comment</comments>
      <pubDate>Tue, 16 Jul 2024 16:09:33 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] JJWT 의존성 오류 &amp;amp; WeakKeyException</title>
      <link>https://anythingis.tistory.com/190</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT 의존성 오류&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;오래된 JJWT 대신 최신에도 업데이트 되는 JJWT API 활용하며 에러가 발생했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT API 의존성 외에도 런타임에 활용될 JJWT Impl,&amp;nbsp; JJWT GSON 의존성을 추가해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래 블로그의 도움을 받았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://atsky.tistory.com/53&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://atsky.tistory.com/53&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720940982400&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;[Spring Boot] JWT 라이브러리를 의존성 추가했음에도 빌드 오류가 나는건 왜일까?(feat. &amp;#96;io.jsonwebtoken:j&quot; data-og-description=&quot;발단흔히 사용하는 jwt 라이브러리인 jjwt 를 사용하여 개발하려 하니 2018년도 이후로 업데이트 되지 않고 있었다. 예제로도 jjwt 를 사용하는 경우가 많지만 최신 패키지인 jjwt-api 가 있기에 이를 &quot; data-og-host=&quot;atsky.tistory.com&quot; data-og-source-url=&quot;https://atsky.tistory.com/53&quot; data-og-url=&quot;https://atsky.tistory.com/53&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxhmzc/hyWzuQQwj4/6k3wJynkjk8krNdbFfsluk/img.png?width=800&amp;amp;height=118&amp;amp;face=0_0_800_118,https://scrap.kakaocdn.net/dn/duHCQv/hyWzCOS6DB/1e5rH9YJw9EazM67IrS3DK/img.png?width=800&amp;amp;height=118&amp;amp;face=0_0_800_118&quot;&gt;&lt;a href=&quot;https://atsky.tistory.com/53&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://atsky.tistory.com/53&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxhmzc/hyWzuQQwj4/6k3wJynkjk8krNdbFfsluk/img.png?width=800&amp;amp;height=118&amp;amp;face=0_0_800_118,https://scrap.kakaocdn.net/dn/duHCQv/hyWzCOS6DB/1e5rH9YJw9EazM67IrS3DK/img.png?width=800&amp;amp;height=118&amp;amp;face=0_0_800_118');&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;[Spring Boot] JWT 라이브러리를 의존성 추가했음에도 빌드 오류가 나는건 왜일까?(feat. `io.jsonwebtoken:j&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;발단흔히 사용하는 jwt 라이브러리인 jjwt 를 사용하여 개발하려 하니 2018년도 이후로 업데이트 되지 않고 있었다. 예제로도 jjwt 를 사용하는 경우가 많지만 최신 패키지인 jjwt-api 가 있기에 이를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;atsky.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT Impl 의존성 추가 안했을 때&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1720941346345&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Unable to load class named [io.jsonwebtoken.impl.DefaultJwtBuilder] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.  Have you remembered to include the jjwt-impl.jar in your runtime classpath?
	at io.jsonwebtoken.lang.Classes.forName(Classes.java:93) ~[jjwt-api-0.11.5.jar:0.11.5]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Jwts.compact() 실행시&amp;nbsp; impl 패키지 내의 DefaultJwtBuilder 인스턴스화 시도하기 때문에 JJWT Impl 의존성 필요&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720941405988&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
 
package io.jsonwebtoken;
import io.jsonwebtoken.lang.Classes;

public final class Jwts {

    private static final Class[] MAP_ARG = new Class[]{Map.class};

    private Jwts() {
    }

    public static JwtBuilder builder() {
        return Classes.newInstance(&quot;io.jsonwebtoken.impl.DefaultJwtBuilder&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT GSON 의존성 추가 안했을 때&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT Serializer 인터페이스는 활용하는데, 구현체가 없어서 예외가 발생합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720940849986&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;io.jsonwebtoken.lang.UnknownClassException: Unable to find an implementation for interface io.jsonwebtoken.io.Serializer using java.util.ServiceLoader.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Jwts.compact() -&amp;gt; DefaultJwtBuilder.compact()&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720941727725&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class DefaultJwtBuilder implements JwtBuilder {

    private Header header;
    private Claims claims;
    private String payload;

    private SignatureAlgorithm algorithm;
    private Key key;

    private Serializer&amp;lt;Map&amp;lt;String,?&amp;gt;&amp;gt; serializer;
    
    @Override
    public String compact() {

        if (this.serializer == null) {
            // try to find one based on the services available
            // TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
            // use the previous commented out line instead
            
            // Serializer 인터페이스 로딩
            this.serializer = LegacyServices.loadFirst(Serializer.class);
        }
        ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT API의 인터페이스&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720941760653&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.jsonwebtoken.io;

public interface Serializer&amp;lt;T&amp;gt; {

    byte[] serialize(T t) throws SerializationException;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JJWT Jackson에 포함된 JJWT API의 인터페이스 구현체&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720941919368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.jsonwebtoken.jackson.io;

public class JacksonSerializer&amp;lt;T&amp;gt; implements Serializer&amp;lt;T&amp;gt; {

    static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper();

    private final ObjectMapper objectMapper;

    @SuppressWarnings(&quot;unused&quot;) //used via reflection by RuntimeClasspathDeserializerLocator
    public JacksonSerializer() {
        this(DEFAULT_OBJECT_MAPPER);
    }

    @SuppressWarnings(&quot;WeakerAccess&quot;) //intended for end-users to use when providing a custom ObjectMapper
    public JacksonSerializer(ObjectMapper objectMapper) {
        Assert.notNull(objectMapper, &quot;ObjectMapper cannot be null.&quot;);
        this.objectMapper = objectMapper;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        Assert.notNull(t, &quot;Object to serialize cannot be null.&quot;);
        try {
            return writeValueAsBytes(t);
        } catch (JsonProcessingException e) {
            String msg = &quot;Unable to serialize object: &quot; + e.getMessage();
            throw new SerializationException(msg, e);
        }
    }

    @SuppressWarnings(&quot;WeakerAccess&quot;) //for testing
    protected byte[] writeValueAsBytes(T t) throws JsonProcessingException {
        return this.objectMapper.writeValueAsBytes(t);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WeakKeyException&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HS512 알고리즘을 활용할 때 키는 512bits == 64bytes 보다 커야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;키가 너무 짧아서 나는 예외입니다. 아래와 같이 적절한 키를 만들거나 임의의 64바이트를 넘는 값을 지정하시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(&lt;span style=&quot;color: #aaaaaa; text-align: right;&quot;&gt;영어, 숫자, 특수문자, 띄어쓰기, 엔터키 1바이트 / 한글 3바이트이지만 ... 키에 특수문자, 띄어쓰기 들어가면 예외터집니다.&lt;/span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);

// 키를 Base64로 인코딩하여 출력 (예: 환경 변수 또는 설정 파일에 저장)
String base64EncodedKey = Encoders.BASE64.encode(key.getEncoded());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <category>개발로그</category>
      <category>JJWT</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/190</guid>
      <comments>https://anythingis.tistory.com/190#entry190comment</comments>
      <pubDate>Sun, 14 Jul 2024 16:26:45 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security 6v 이상, AuthenticatonManager 생성 방법</title>
      <link>https://anythingis.tistory.com/189</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;6v 아래로는 스프링 시큐리티 의존성 사용시 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WebSecurityConfigurerAdapter의 authenticationManager()&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;AuthenticationConfiguration.getAuthenticationManager()&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;등을&amp;nbsp; 활용했는데 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;6v 이상 오면서 별도의 방법이 필요합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;WebSecurityConfigurerAdapter 대신 SecurityFilterChain을 빈 등록해줘야하고, 해당 메소드의 파라미터인 HttpSecurity를 통해 설정에서 사용되는 공유객체를 가져와 설정할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동작 방식&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;공유 객체 검색&lt;/b&gt;: http.getSharedObject(AuthenticationManagerBuilder.class)는 HttpSecurity 객체 내에 공유된 AuthenticationManagerBuilder 인스턴스를 검색합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;구성 요소 설정&lt;/b&gt;: 검색된 AuthenticationManagerBuilder 객체를 사용하여 사용자 정의 설정을 적용합니다. 예를 들어, 사용자 세부 정보 서비스(UserService)와 비밀번호 인코더(BCryptPasswordEncoder)를 설정합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Authenticatin Manager 생성&lt;/b&gt;: 설정된 AuthenticationManagerBuilder 객체를 사용하여 AuthenticationManager 인스턴스를 생성합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1720932749070&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurity {

    private final UserService userService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // AuthenticationManagerBuilder 생성
        AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
        
	// AuthenticationManager 생성
        AuthenticationManager authenticationManager = authenticationManagerBuilder.build();

        http.csrf(AbstractHttpConfigurer::disable);

        http.authorizeHttpRequests((request) -&amp;gt;  request
                .requestMatchers(antMatcher(&quot;/actuator/**&quot;)).permitAll()
             ).authenticationManager(authenticationManager)
                .sessionManagement((session) -&amp;gt; session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
			
            // AuthenticationManager 활용
        http.addFilter(getAuthenticationFilter(authenticationManager));

        return http.build();
    }

    private AuthenticationFilter getAuthenticationFilter(AuthenticationManager authenticationManager){
            AuthenticationFilter authenticationFilter = new AuthenticationFilter();
            authenticationFilter.setAuthenticationManager(authenticationManager);

            return authenticationFilter;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;목적&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;객체 공유&lt;/b&gt;: 여러 곳에서 동일한 객체를 사용하여 일관된 설정을 유지할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;설정 간소화&lt;/b&gt;: 개별적으로 객체를 생성하고 설정할 필요 없이, Spring Security가 제공하는 공유 객체를 사용하여 설정을 간소화합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720932835164&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;Spring Security without the WebSecurityConfigurerAdapter&quot; data-og-description=&quot;In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter, as we encourage users to move towards a component-based security configuration. To assist with the transition to this new style of configuration, we have compiled a list of common &quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter&quot; data-og-url=&quot;https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GD3nz/hyWzs6wSob/FqNsdIdgKiHGHKtE4eP0yK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bFR36u/hyWzAKfPN8/gbuLq0tETGYdKtKw6kGw31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GD3nz/hyWzs6wSob/FqNsdIdgKiHGHKtE4eP0yK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bFR36u/hyWzAKfPN8/gbuLq0tETGYdKtKw6kGw31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;Spring Security without the WebSecurityConfigurerAdapter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter, as we encourage users to move towards a component-based security configuration. To assist with the transition to this new style of configuration, we have compiled a list of common&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;spring.io&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;</description>
      <category>개발/스프링</category>
      <category>spring security 6</category>
      <category>springboot 2.7</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/189</guid>
      <comments>https://anythingis.tistory.com/189#entry189comment</comments>
      <pubDate>Sun, 14 Jul 2024 14:10:52 +0900</pubDate>
    </item>
    <item>
      <title>SpringBoot 2.4v 이상 H2 DB url이 아닌 랜덤 문자열로 설정될 때</title>
      <link>https://anythingis.tistory.com/188</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래와 같이 db 이름을 설정했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720770028019&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  application:
    name: catalog-service
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console
  jpa:
    hibernate:
      ddl-auto: create-drop
    datasource:
      driver-class-name: org.h2.Driver
      url: jdbc:h2:mem:testdb
      username: sa
      password:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VGL3y/btsIwPvLxH8/NKYcelOZ8DErZWmi5YqrSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VGL3y/btsIwPvLxH8/NKYcelOZ8DErZWmi5YqrSK/img.png&quot; data-alt=&quot;DB 이름 달라짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VGL3y/btsIwPvLxH8/NKYcelOZ8DErZWmi5YqrSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVGL3y%2FbtsIwPvLxH8%2FNKYcelOZ8DErZWmi5YqrSK%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;1158&quot; height=&quot;101&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DB 이름 달라짐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHADrC/btsIyvbuOoU/7NpXfCOkp2ni9jIAnK2b01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHADrC/btsIyvbuOoU/7NpXfCOkp2ni9jIAnK2b01/img.png&quot; data-alt=&quot;설정파일대로 로그인 안됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHADrC/btsIyvbuOoU/7NpXfCOkp2ni9jIAnK2b01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHADrC%2FbtsIyvbuOoU%2F7NpXfCOkp2ni9jIAnK2b01%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;1143&quot; height=&quot;571&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;설정파일대로 로그인 안됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SpringBoot 2.4버젼 이후로 추가적으로 application 설정이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720769994951&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring: 
  datasource:
    generate-unique-name: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/188</guid>
      <comments>https://anythingis.tistory.com/188#entry188comment</comments>
      <pubDate>Fri, 12 Jul 2024 16:44:34 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] Spring Clound Gateway 사용하는 이유 &amp;amp; 설정</title>
      <link>https://anythingis.tistory.com/187</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Why GateWay?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Gateway 없을 때&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;client.jpg&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KIPh8/btsIvaSWDBK/19SsiFM0Z7dcpkUFCIYAl0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KIPh8/btsIvaSWDBK/19SsiFM0Z7dcpkUFCIYAl0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KIPh8/btsIvaSWDBK/19SsiFM0Z7dcpkUFCIYAl0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKIPh8%2FbtsIvaSWDBK%2F19SsiFM0Z7dcpkUFCIYAl0%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;1100&quot; height=&quot;496&quot; data-filename=&quot;client.jpg&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Gateway 있을 때&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;client + gateway.jpg&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkrkMI/btsIv5QUAJs/7bpj12KHaxjDnNw5yZYCQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkrkMI/btsIv5QUAJs/7bpj12KHaxjDnNw5yZYCQ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkrkMI/btsIv5QUAJs/7bpj12KHaxjDnNw5yZYCQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkrkMI%2FbtsIv5QUAJs%2F7bpj12KHaxjDnNw5yZYCQ1%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;1067&quot; height=&quot;533&quot; data-filename=&quot;client + gateway.jpg&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Gateway 자체도 Eureka Client 의존성 포함해야함(Eureka Server에 등록되어야 하기 때문)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byx4Si/btsIuPIq00G/KJb0QT4sNKm1zEXRClxDqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byx4Si/btsIuPIq00G/KJb0QT4sNKm1zEXRClxDqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byx4Si/btsIuPIq00G/KJb0QT4sNKm1zEXRClxDqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyx4Si%2FbtsIuPIq00G%2FKJb0QT4sNKm1zEXRClxDqk%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;969&quot; height=&quot;773&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;773&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;959&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RuaHO/btsIvz5UsjO/kygm0fdK7SZohw0urXcww1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RuaHO/btsIvz5UsjO/kygm0fdK7SZohw0urXcww1/img.png&quot; data-alt=&quot;Gateway(Tomcat) X -&amp;amp;gt; Reactive Gateway(Netty)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RuaHO/btsIvz5UsjO/kygm0fdK7SZohw0urXcww1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRuaHO%2FbtsIvz5UsjO%2Fkygm0fdK7SZohw0urXcww1%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;959&quot; height=&quot;770&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Gateway(Tomcat) X -&amp;gt; Reactive Gateway(Netty)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1720620216894&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 8000

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka

spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      routes:
        - id: user-service # gateway 등록 id
          uri: lb://USER-SERVICE  # 포워딩할 주소, Eureka Server에 등록된 포워딩할 Eureka Client spring.application.name 
          predicates:
            - Path=/user-service/** # user-service로 시작되는 요청 모두 포워딩&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/187</guid>
      <comments>https://anythingis.tistory.com/187#entry187comment</comments>
      <pubDate>Wed, 10 Jul 2024 23:16:20 +0900</pubDate>
    </item>
    <item>
      <title>[MSA] Eureka Server- Client</title>
      <link>https://anythingis.tistory.com/186</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Eureka Server Service 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcAzmO/btsIsK1xmu8/CWE3ckKcNduTRGiiZ8sNnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcAzmO/btsIsK1xmu8/CWE3ckKcNduTRGiiZ8sNnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcAzmO/btsIsK1xmu8/CWE3ckKcNduTRGiiZ8sNnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcAzmO%2FbtsIsK1xmu8%2FCWE3ckKcNduTRGiiZ8sNnK%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;974&quot; height=&quot;780&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;780&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;485&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMqeVs/btsIs0iKrV0/5p81Vs4WqU7TaqYqDBkDZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMqeVs/btsIs0iKrV0/5p81Vs4WqU7TaqYqDBkDZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMqeVs/btsIs0iKrV0/5p81Vs4WqU7TaqYqDBkDZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMqeVs%2FbtsIs0iKrV0%2F5p81Vs4WqU7TaqYqDBkDZk%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;485&quot; height=&quot;351&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_eurekaserver.png&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq4yNf/btsItWs4Gsd/kua8sqcyEeubl6QJ764xr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq4yNf/btsItWs4Gsd/kua8sqcyEeubl6QJ764xr1/img.png&quot; data-alt=&quot;어노테이션 필수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq4yNf/btsItWs4Gsd/kua8sqcyEeubl6QJ764xr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq4yNf%2FbtsItWs4Gsd%2Fkua8sqcyEeubl6QJ764xr1%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;1130&quot; height=&quot;252&quot; data-filename=&quot;edited_eurekaserver.png&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;어노테이션 필수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Eureka Client Service 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FYyP5/btsIsbL3hz3/5k80sf3S67eCGqh5UL3B71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FYyP5/btsIsbL3hz3/5k80sf3S67eCGqh5UL3B71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FYyP5/btsIsbL3hz3/5k80sf3S67eCGqh5UL3B71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFYyP5%2FbtsIsbL3hz3%2F5k80sf3S67eCGqh5UL3B71%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;976&quot; height=&quot;783&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;783&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;981&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ej44xr/btsIt4R1Byr/TOsm8aPxfLeevI84dbs9wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ej44xr/btsIt4R1Byr/TOsm8aPxfLeevI84dbs9wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ej44xr/btsIt4R1Byr/TOsm8aPxfLeevI84dbs9wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fej44xr%2FbtsIt4R1Byr%2FTOsm8aPxfLeevI84dbs9wk%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;981&quot; height=&quot;794&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;794&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-filename=&quot;edited_2.png&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dP3TEI/btsItWGDWqw/XqvLoZykjt8ksXo4h5ew21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dP3TEI/btsItWGDWqw/XqvLoZykjt8ksXo4h5ew21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dP3TEI/btsItWGDWqw/XqvLoZykjt8ksXo4h5ew21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdP3TEI%2FbtsItWGDWqw%2FXqvLoZykjt8ksXo4h5ew21%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;1110&quot; height=&quot;432&quot; data-filename=&quot;edited_2.png&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;432&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;1036&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LDCm3/btsIsYZzkkF/QUr99L4wvgeBoyNbdIHDEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LDCm3/btsIsYZzkkF/QUr99L4wvgeBoyNbdIHDEk/img.png&quot; data-alt=&quot;server.port : 0 == random&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LDCm3/btsIsYZzkkF/QUr99L4wvgeBoyNbdIHDEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLDCm3%2FbtsIsYZzkkF%2FQUr99L4wvgeBoyNbdIHDEk%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;1036&quot; height=&quot;503&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;server.port : 0 == random&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;server.port: 0으로 지정시 실행시킬 때마다 포트 랜덤 지정됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동일한 서비스의 인스턴스를 따로 관리하기 위해 랜덤값 지정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Eureka Server - Client 순으로 기동&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCpfCU/btsItCojlEn/NpLZd4oOzMSEF2XWHlIJwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCpfCU/btsItCojlEn/NpLZd4oOzMSEF2XWHlIJwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCpfCU/btsItCojlEn/NpLZd4oOzMSEF2XWHlIJwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCpfCU%2FbtsItCojlEn%2FNpLZd4oOzMSEF2XWHlIJwK%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;1059&quot; height=&quot;412&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkP0xB/btsIruedyJX/Y1alQcFF7U2NkPp1CAZye0/img.png&quot; data-filename=&quot;local.png&quot; data-origin-height=&quot;968&quot; data-origin-width=&quot;1887&quot; data-is-animation=&quot;false&quot; /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;serv1.png&quot; data-origin-width=&quot;1289&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baRM2a/btsItUhJ7yK/lkPDTjjatAutQWMJ8aAfW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baRM2a/btsItUhJ7yK/lkPDTjjatAutQWMJ8aAfW0/img.png&quot; data-alt=&quot;실행마다 랜덤포트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baRM2a/btsItUhJ7yK/lkPDTjjatAutQWMJ8aAfW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaRM2a%2FbtsItUhJ7yK%2FlkPDTjjatAutQWMJ8aAfW0%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;674&quot; data-filename=&quot;serv1.png&quot; data-origin-width=&quot;1289&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행마다 랜덤포트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720512409154&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;Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA) 강의 | Dowon Lee - 인프런&quot; data-og-description=&quot;Dowon Lee | Spring framework의 Spring Cloud 제품군을 이용하여 마이크로서비스 애플리케이션을 개발해 보는 과정입니다. Cloud Native Application으로써의 Spring Cloud를 어떻게 사용하는지, 구성을 어떻게 하는&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/스프링-클라우드-마이크로서비스&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Jkl48/hyWzvVyhc3/kFhtYdGnRPKMxI6VZD5eKK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bnbAHe/hyWvVO9aWO/HAuCa3KZQXDISIkk5cDrf0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/tYbb0/hyWzEdS8Cy/jYTy6IkY3V5156hVPUURRK/img.png?width=1200&amp;amp;height=400&amp;amp;face=0_0_1200_400&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Jkl48/hyWzvVyhc3/kFhtYdGnRPKMxI6VZD5eKK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/bnbAHe/hyWvVO9aWO/HAuCa3KZQXDISIkk5cDrf0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/tYbb0/hyWzEdS8Cy/jYTy6IkY3V5156hVPUURRK/img.png?width=1200&amp;amp;height=400&amp;amp;face=0_0_1200_400');&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;Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA) 강의 | Dowon Lee - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Dowon Lee | Spring framework의 Spring Cloud 제품군을 이용하여 마이크로서비스 애플리케이션을 개발해 보는 과정입니다. Cloud Native Application으로써의 Spring Cloud를 어떻게 사용하는지, 구성을 어떻게 하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.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;</description>
      <category>개발로그</category>
      <category>MSA</category>
      <category>spring cloud</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/186</guid>
      <comments>https://anythingis.tistory.com/186#entry186comment</comments>
      <pubDate>Tue, 9 Jul 2024 17:06:53 +0900</pubDate>
    </item>
    <item>
      <title>삼성 청년 SW 아카데미 10기 수료 후기</title>
      <link>https://anythingis.tistory.com/182</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/162&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/162&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718605265524&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;[SSAFY] 전공자 10기 합격 후기&quot; data-og-description=&quot;SSAFY 면접 내용이나 코딩테스트 내용은 공유가 불가능합니다 스펙 국립 지방대 컴공 졸업(학점 3.96/4.5) 교내 코딩테스트 수상경험 1회 웹 개발 관련 프로젝트 2회 정보처리기사 볼 품이 없습니다 &quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/162&quot; data-og-url=&quot;https://anythingis.tistory.com/162&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/exwGht/hyWlnd3aKZ/3IFvONTwRaF0o2EpcYBUW0/img.jpg?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bpX7R2/hyWldCsjqB/ZB6pxo307kMKBlaWrZ2Tmk/img.jpg?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bKB4LQ/hyWlcXTc8h/KZPLKeufHskFIWoe3yYD5k/img.png?width=618&amp;amp;height=422&amp;amp;face=0_0_618_422&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/162&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/162&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/exwGht/hyWlnd3aKZ/3IFvONTwRaF0o2EpcYBUW0/img.jpg?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bpX7R2/hyWldCsjqB/ZB6pxo307kMKBlaWrZ2Tmk/img.jpg?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bKB4LQ/hyWlcXTc8h/KZPLKeufHskFIWoe3yYD5k/img.png?width=618&amp;amp;height=422&amp;amp;face=0_0_618_422');&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;[SSAFY] 전공자 10기 합격 후기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSAFY 면접 내용이나 코딩테스트 내용은 공유가 불가능합니다 스펙 국립 지방대 컴공 졸업(학점 3.96/4.5) 교내 코딩테스트 수상경험 1회 웹 개발 관련 프로젝트 2회 정보처리기사 볼 품이 없습니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;합격 후기를 쓴지도 벌써 1년이 되어 수료 후기를 쓰게 됐습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java 트랙의 관점에서 느낀 점들이니 다들 참고하고 지원해보셨으면 좋겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 팀 프로젝트 경험&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획, 설계, 개발, 배포 &amp;amp; 운영, 발표의 라이프사이클을 6주~7주 라는 짧은 시간동안 반복하면서 다양한 이해관계자의 입장을 경험해봤습니다.&amp;nbsp;&amp;nbsp;이런 과정에서 한 가지 기능에 대해 팀원들이 디테일한 부분은 다르게 생각할 수 있기에 기획에서 개발까지 이어지는 &lt;b&gt;소통을 통한 문서화의 중요성&lt;/b&gt;을 느꼈습니다. 또,&amp;nbsp;진행상황에 대해 투명하게 공유하고, 막히는 부분에서는 고민해보고 팀원들과 협의해 &lt;b&gt;유연하게 대응하는 법&lt;/b&gt;을 배웠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획 &amp;amp; 설계&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;1a282032-3b4b-42b7-b1d6-4b1f0af9625c&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 기획을 왜 ?&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;개발을 잘하려면 기획(요구사항)을 잘 이해하고 설계해야 목적에 맞는 서비스를 만들 수 있다고 생각합니다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이를 위해서는 기획 분야에 대한 지식이 필요합니다. 주로 팀원들이 관심있는 도메인을 선택했고, 추가적으로 인터뷰와 문의를 여러 차례 진행했습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 기획&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 기획은 주로 어떤 문제를 어떻게 해결할 것인지에 대한 고민에서 시작합니다. 사회 이슈나 관심 있는 도메인에서 해결이 필요한 문제나 유용할 만한 기능을 찾습니다. 예를 들어, &lt;b&gt;작품 설정 시각화 서비스&lt;/b&gt;를 기획하면서 &lt;b&gt;네이버 웹툰 작가와 대면 인터뷰&lt;/b&gt;를 진행했고, &lt;b&gt;22대 대선을 앞두고 국회의원의 활동 정보를 제공하는 서비스&lt;/b&gt;를 기획하면서 &lt;b&gt;선관위와 한국매니페스토 본부&lt;/b&gt;에 문의해 요구사항을 파악하고 법적 제약을 조사했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음으로, 콘텐츠를 어떻게 제공할지 고민합니다. 콘텐츠 제공 방식에는 개발자가 만들어낸 콘텐츠를 사용자가 소비하는 &lt;b&gt;능동적 콘텐츠&lt;/b&gt;와 사용자가 생성하고 소비하는 &lt;b&gt;수동적 콘텐츠&lt;/b&gt;가 있습니다. 능동적 콘텐츠는 기본적인 즐길거리를 제공해 유입이 쉽지만, 자동화된 제공 방식에서는 퀄리티에 한계가 있습니다. 이를 빅데이터 분석과 AI를 활용해 보완할 수 있습니다. 수동적 콘텐츠는 유입이 어렵지만, 사용자 수가 많아지면 서비스의 가치가 높아집니다. &lt;i&gt;&lt;b&gt;개인적으로는 능동적 콘텐츠를 제공하는 것이 서비스의 흐름이 덜 복잡하고 목표를 명확히 설정하는 데 유리하다고 느꼈습니다.&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;화면 기획&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;화면 기획에도 적극적으로 참여했습니다. 주로 백엔드를 담당했지만, 화면에서 어떻게 보여줄지에 따라 API 설계가 달라지기 때문에 사용자 입장에서 생각하고 아이디어를 제시했습니다. 주로 온보딩 페이지와 메인 기능 페이지를 먼저 설계하고, 다양한 화면에서 재활용할 수 있는 컴포넌트를 기획합니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIBtdq/btsIO3FFDlt/uL57xWaqZpABXWGsniSnzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIBtdq/btsIO3FFDlt/uL57xWaqZpABXWGsniSnzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIBtdq/btsIO3FFDlt/uL57xWaqZpABXWGsniSnzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIBtdq%2FbtsIO3FFDlt%2FuL57xWaqZpABXWGsniSnzK%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;584&quot; height=&quot;773&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;화면과 체크리스트를 통해 어떤 기능들이 동작할지까지 디테일을 잡습니다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획의 어려움&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획할 때 가장 어려웠던 점은 세상에 완전히 새로운 서비스는 없다는 점입니다. 그럼에도 불구하고 창의적인 기획을 통해 프로젝트를 재미있게 진행할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1. 페어 프로젝트 / Yesterdays' Trip/ 관광지 리뷰 제공 &amp;amp; 관심 지역 알림 서비스 / 최우수상&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720185088058&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/yesterday-s-Trip: 여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스&quot; data-og-description=&quot;여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스. Contribute to JaegeonYu/yesterday-s-Trip development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; data-og-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpZP2O/hyWvW0LfnM/tdHdydgkslF4CbAyDpLK2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpZP2O/hyWvW0LfnM/tdHdydgkslF4CbAyDpLK2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/yesterday-s-Trip: 여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;여행지, 축제 관련 리뷰 서비스 &amp;amp; 지역 팔로우를 통한 알림 서비스. Contribute to JaegeonYu/yesterday-s-Trip development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/PerspectiView&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2. 첫 팀 프로젝트 / 시작 / 작품 설정 시각화 서비스&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720185092543&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/PerspectiView: 시각적으로 보는 작품, 시작&quot; data-og-description=&quot;시각적으로 보는 작품, 시작. Contribute to JaegeonYu/PerspectiView development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/PerspectiView&quot; data-og-url=&quot;https://github.com/JaegeonYu/PerspectiView&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9jV27/hyWvNbG15l/knIj4P9T8ZZZSEYjnQD4A1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/PerspectiView&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/PerspectiView&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9jV27/hyWvNbG15l/knIj4P9T8ZZZSEYjnQD4A1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/PerspectiView: 시각적으로 보는 작품, 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;시각적으로 보는 작품, 시작. Contribute to JaegeonYu/PerspectiView development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/HoneyPot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3. 두번째 팀 프로젝트 / 여의도 꿀통 / 국회의원 공약 이행현황, 활동정보 제공 서비스 / 우수상 / 800명 이상의 사용자 경험&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720185090746&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/HoneyPot: 국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스&quot; data-og-description=&quot;국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스, 여의도 꿀통. Contribute to JaegeonYu/HoneyPot development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/HoneyPot&quot; data-og-url=&quot;https://github.com/JaegeonYu/HoneyPot&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MBPh2/hyWvPHlzd4/238X5m1Nkmvh8LyhZYSbL0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/HoneyPot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/HoneyPot&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MBPh2/hyWvPHlzd4/238X5m1Nkmvh8LyhZYSbL0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/HoneyPot: 국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스, 여의도 꿀통. Contribute to JaegeonYu/HoneyPot development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/sharep&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4.&amp;nbsp; 세 번째 팀 프로젝트 / Sharep / 소규모 프로젝트 협업 서비스&amp;nbsp;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720185096591&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/sharep: 하나의 레시피에서  출발하는 프로젝트의 완성&quot; data-og-description=&quot;하나의 레시피에서 출발하는 프로젝트의 완성. Contribute to JaegeonYu/sharep development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/sharep&quot; data-og-url=&quot;https://github.com/JaegeonYu/sharep&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RtUU3/hyWvNCMJmr/WPruix1lDMXdIkCcd3VYr1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/sharep&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/sharep&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RtUU3/hyWvNCMJmr/WPruix1lDMXdIkCcd3VYr1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/sharep: 하나의 레시피에서 출발하는 프로젝트의 완성&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;하나의 레시피에서 출발하는 프로젝트의 완성. Contribute to JaegeonYu/sharep development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java 기반의 문법부터 다시 다지고, 컴퓨팅 알고리즘을 거쳐서 웹개발 교육으로 이어집니다. &lt;i&gt;&lt;b&gt;어떤 교육이든 레거시부터 시작&lt;/b&gt;&lt;/i&gt;해서 어떤점이 달라졌는지 명확히 알 수 있었고 &lt;i&gt;&lt;b&gt;추상화된 기술의 동작원리를 이해&lt;/b&gt;&lt;/i&gt;하면서 프레임워크를 더 잘 활용하는법, 프레임워크에서 발생하는 에러의 원인을 찾는 역량을 기를 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;SpringBoot, JPA와 같은 최신 기술을 잘 활용하기 위해서 JSP, Mybatis 같은 기술을 배척하지 말고 그 밑단이 어떻게 이뤄져있는지, 어떻게 발전해왔는지 관심을 가져보는게 도움이 될 수 있다는 걸 배웠습니다.&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/165&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720440388579&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;[Spring] Spring without Spring Boot&quot; data-og-description=&quot;스프링부트로 스프링을 접해서 몰랐는데 레거시 스프링을 접하게 되면서 정말 스프링부트는 미쳤다는 걸 체감하고 있다. 서블렛 컨테이너 등록 VS 내장 서블렛 컨테이너 레거시는 별도의 서블&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/165&quot; data-og-url=&quot;https://anythingis.tistory.com/165&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgzrEv/hyWzwz2zOc/Y8AkxriSm52IgWEK3rD7Gk/img.png?width=700&amp;amp;height=300&amp;amp;face=49_65_170_197,https://scrap.kakaocdn.net/dn/bz4KmP/hyWzDeRNcB/WQDisyJB3jqwiULDYXUMh1/img.png?width=700&amp;amp;height=300&amp;amp;face=49_65_170_197,https://scrap.kakaocdn.net/dn/l4Dl6/hyWvRMAJE6/fRUPzpMF9KXUUtDXZoSi2K/img.png?width=1292&amp;amp;height=575&amp;amp;face=0_0_1292_575&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/165&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgzrEv/hyWzwz2zOc/Y8AkxriSm52IgWEK3rD7Gk/img.png?width=700&amp;amp;height=300&amp;amp;face=49_65_170_197,https://scrap.kakaocdn.net/dn/bz4KmP/hyWzDeRNcB/WQDisyJB3jqwiULDYXUMh1/img.png?width=700&amp;amp;height=300&amp;amp;face=49_65_170_197,https://scrap.kakaocdn.net/dn/l4Dl6/hyWvRMAJE6/fRUPzpMF9KXUUtDXZoSi2K/img.png?width=1292&amp;amp;height=575&amp;amp;face=0_0_1292_575');&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;[Spring] Spring without Spring Boot&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링부트로 스프링을 접해서 몰랐는데 레거시 스프링을 접하게 되면서 정말 스프링부트는 미쳤다는 걸 체감하고 있다. 서블렛 컨테이너 등록 VS 내장 서블렛 컨테이너 레거시는 별도의 서블&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://anythingis.tistory.com/167&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720440403610&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;[Mybatis] JPA 고수인 내가 Mybatis 세계에선 뉴비?&quot; data-og-description=&quot;사실 JPA 고수 아닙니다. 자바 백엔드 시작을 김영한님의 강의를 보고 시작한만큼 DB접근 기술을 JPA밖에 몰랐었고, 올해 초에 프로그래머스에서 진행한 단순 CRUD 구현을 그만 ! 스터디를 들으면서&quot; data-og-host=&quot;anythingis.tistory.com&quot; data-og-source-url=&quot;https://anythingis.tistory.com/167&quot; data-og-url=&quot;https://anythingis.tistory.com/167&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bAcsgV/hyWzuhVnqd/Z6oUYRik9GHWrFLHMJElQk/img.jpg?width=300&amp;amp;height=168&amp;amp;face=147_31_170_56,https://scrap.kakaocdn.net/dn/dn4m5b/hyWzyq5ZId/8RXTePcX5VVO3qmAZOgZg1/img.jpg?width=300&amp;amp;height=168&amp;amp;face=147_31_170_56,https://scrap.kakaocdn.net/dn/e4vtm/hyWzwz2Eqw/yQWUUE4jaUKEk0eHWZv7a1/img.png?width=620&amp;amp;height=433&amp;amp;face=0_0_620_433&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://anythingis.tistory.com/167&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bAcsgV/hyWzuhVnqd/Z6oUYRik9GHWrFLHMJElQk/img.jpg?width=300&amp;amp;height=168&amp;amp;face=147_31_170_56,https://scrap.kakaocdn.net/dn/dn4m5b/hyWzyq5ZId/8RXTePcX5VVO3qmAZOgZg1/img.jpg?width=300&amp;amp;height=168&amp;amp;face=147_31_170_56,https://scrap.kakaocdn.net/dn/e4vtm/hyWzwz2Eqw/yQWUUE4jaUKEk0eHWZv7a1/img.png?width=620&amp;amp;height=433&amp;amp;face=0_0_620_433');&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;[Mybatis] JPA 고수인 내가 Mybatis 세계에선 뉴비?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;사실 JPA 고수 아닙니다. 자바 백엔드 시작을 김영한님의 강의를 보고 시작한만큼 DB접근 기술을 JPA밖에 몰랐었고, 올해 초에 프로그래머스에서 진행한 단순 CRUD 구현을 그만 ! 스터디를 들으면서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;anythingis.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배포 &amp;amp; 운영&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그동안의 개발을 거의 로컬에서 기능구현하고 배포가 되나 안되나 해보고 끝이였습니다. ssafy를 겪으며 배포환경에 익숙해지고 웹 서비스를 조금 더 넓게 볼 수 있는 관점을 얻고 아래와 같은 경험들을 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;0)&lt;/b&gt; Linux 기반 Script 활용&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;1)&lt;/b&gt;도커 네트워크와 프록시 서버를 활용해 포트 노출을 최소화 하고 내부 서비스 끼리 통신하는 설계&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;2)&lt;/b&gt; 프록시 서버에서 TLS 인증 설정 &lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;3)&lt;/b&gt; 로컬과 배포환경의 CORS 에러 해결 &lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/i&gt;&lt;i&gt;&lt;b&gt;)&lt;/b&gt;도커와 UFW의 iptables 체인 충돌로 인한 체인 순서 설정&lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;5)&lt;/b&gt;젠킨스를 활용한 CI/CD 파이프라인을 구축해 배포자동화 &amp;amp; 환경변수 설정파일을 관리와 배포 성공 여부 메세지 웹훅을 통한 알림&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;네트워크 인프라를 구축하며 여러 서비스가 엮이는 특성상 특별한 에러는 없지만 원하는 대로 동작이 안되거나, 에러가 있지만 로그를 어디서 봐야할지 몰랐었는데 이런 &lt;i&gt;&lt;b&gt;논리적 오류&lt;/b&gt;&lt;/i&gt;를 빠르게 원인파악하고 해결하는 능력을 기를 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjdRGY/btsIrrOyPzs/Xu0gdcfMOaiwo4Y8FWThyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjdRGY/btsIrrOyPzs/Xu0gdcfMOaiwo4Y8FWThyK/img.png&quot; data-alt=&quot;Hitmap&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjdRGY/btsIrrOyPzs/Xu0gdcfMOaiwo4Y8FWThyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjdRGY%2FbtsIrrOyPzs%2FXu0gdcfMOaiwo4Y8FWThyK%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;1280&quot; height=&quot;585&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Hitmap&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;콘텐츠는 서비스 개발자만 제공하고 사용자는 GET 요청만 하는 유입이 쉬운 능동적 콘텐츠 제공 서비스를 이주정도 운영해보고,&amp;nbsp; SSAFY 네트워크 메신저를 통해 홍보하면서 800명 이상의 사용자 트래픽을 경험해봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백엔드는 Nginx 프록시 서버의 순간적으로 사람이 몰릴 때 오류 로그를 보고 Nginx Upstream을 활용한 스케일 아웃을 진행했고, &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프론트는 Google Analytics와 Hitmap을 적용해 사용자의 피드백을 유추해 카테고리 순서를 조정 및 조작 버튼 추가 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;멘토링&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;삼성 계열사의 임직원분들에게 멘토링을 받을 수 있습니다. 평소에도 게시판을 통해서 질의응답이 가능하고 비대면 화상 채팅을 통해 주기적으로 멘토링이 이루어집니다. 직무에 대한 소개, 궁금한 점들을 물어볼 수 있어서 좋은 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상장&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;삼성전자 직인이 찍힌 프로젝트 경진대회 상장을 받을 수 있습니다. 모두가 받는건 아니지만 열심히하면 못받을 영역은 아닙니다.&lt;/span&gt;&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;1376&quot; data-origin-height=&quot;907&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpXK67/btsIrcC0Bzq/Q1ijXi4WADfybP7hTBZxQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpXK67/btsIrcC0Bzq/Q1ijXi4WADfybP7hTBZxQ1/img.png&quot; data-alt=&quot;자소서 두줄 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpXK67/btsIrcC0Bzq/Q1ijXi4WADfybP7hTBZxQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpXK67%2FbtsIrcC0Bzq%2FQ1ijXi4WADfybP7hTBZxQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;397&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;907&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자소서 두줄 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스스로에게 아쉬운 점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본적인 알고리즘, 웹 개발의 기초를 배우는 1학기동안 취준생이라는 마인드가 별로 없이 학교를 다닌 것 같은 느낌입니다. 물론 열심히 쏟은만큼 Java, Spring, Mybatis를 익히며 기본적인 동작원리를 이해하고 프로젝트 최우수상을 수상했지만, 회사 지원에 대한 준비가 아쉬웠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2학기에 3차례의 팀 프로젝트를 진행하며 프로젝트 당 기간이 7주 주어집니다. 첫 프로젝트에서 팀원들과 Jira로 일정관리 및 Git 형상관리, 배포환경 구축, 배포 자동화를 처음 접해보면서 죽을만큼 갈리고 엄청난 성장을 했던 것에 비해 2, 3차례 프로젝트에서는 완만하게 성장한 것 같습니다. 익숙해진 것이라고도 말할 수 있겠지만 아직 배울 것이 많은 취준생, 신입의 입장에서 아쉬운점이라고 생각합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;혼자 공부하던 시절에 비해서 블로그에 포스팅하는 것도 뜸했던 것이 아쉽습니다. 많은 문제를 해결했지만 나중에 뒤돌아서 저의 경험을 정리할 때 명확히 생각이 나지 않는달까요. 똑같은 문제를 만나면 어떻게 해결하는지 알지만 그때그때 기록하는 것의 중요성을 다시 한번 깨닫게 된 것 같습니다...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 곧 12기가 시작된다고 알고 있습니다. 다들 화이팅입니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <category>SSAFY</category>
      <category>삼성 청년 SW 아카데미</category>
      <category>수료</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/182</guid>
      <comments>https://anythingis.tistory.com/182#entry182comment</comments>
      <pubDate>Fri, 5 Jul 2024 22:10:56 +0900</pubDate>
    </item>
    <item>
      <title>2장 나를 제대로 이해하라</title>
      <link>https://anythingis.tistory.com/185</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인생기술서를 통해 나만의 생각노트를 만들어보자&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;엑실, 경험 리스트로 정리하면 지원자 개개인의 유연한 사고와 응용의 자유를 가로막는 장애물이 되는 경우가 많다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #ee2323;&quot;&gt;인생기술서를 작성해보자&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자기만의 레시피를 만들기 위해 각종 재료를 찾고, 재료들의 기능과 역할을 제대로 이해해가는 과정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;취업중심적 사고는 배제하고, 어떤 제약도 없이 자신에 대한 모든 것을 돌아봐야한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;특히 공모전, 대외활동, 학교 수업 중에 진행했던 각종 프로젝트, 리포트, 논문, 과제들까지 모두 뒤져보고 정리하는 것도 중요하다. 당시 수행했던 &lt;i&gt;&lt;b&gt;프로젝트나&lt;/b&gt; &lt;b&gt;과제의 배경은 무엇이었는지, 어떤 문제의식을 가졌는지, 어떻게 접근하고 해결해야 한다고 생각&lt;/b&gt;&lt;/i&gt;했는지에 해당하는 구체적인 내용들을 흐름에 맞게 작성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인생기술서를 작성할 때는 각 재료들을 어떻게 해석하고 활용할지 고민하지 않는 것이 핵심이다. 최대한 당시의 상황, 지식, 경험, 감정과 기분까지 세세하게 있는 그대로 드러내야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인생기술서 작성의 궁극적인 목적은 일차적으로 지난 인생을 전체적으로 되돌아보고 자기만의 색깔, 자기만의 재료를 찾는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후에 지속적인 고찰을 통해 자기만의 생각근육을 만들어 단단하게 키워나가야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;평범한 재료에 대한 색다른 해석이 제품을 더욱 빛나게 한다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;대부분 자소서를 쓸 때 '소재를 어떤 식으로 쓰면 좋겠다'는 재료 중심적인 생각이 아니라, 회사의 인재상 또는 직무역량이라는 틀에 맞춰 경험을 욱여넣는 식으로 접근한다. 이는 자소서의 재미와 차별성을 오히려 반감시킨다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;자신이 '무엇'을 갖고 있는지에 대한 이해, 그리고 이를 '어떻게' 활용할 것이냐가 취업에서 중요하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;평범하고, 애매하고, 추상적이고, 차별성 없는 자소서에서 벗어나고 싶다면 2가지를 명심해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;첫째, 본인에게 없는 경험을 어떻게 채울지 고민하기보다 자신이 가진 재료와 경험을 어떻게 활용할지 고민하는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;둘째, 인재상 같은 정형화된 틀에 억지로 경험을 짜 맞추지 않고, 경험을 자기 방식대로 해석해 주제를 제시하는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;재료를 한 번 조리하는 것만으로 끝이 아닌, 어떻게 조리하고 해석하느냐에 따라 다양한 주제에서 사용될 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인생기술서를 잘못 활용하면 좋은 재료로도 음식을 망친다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재료를 살리지 못하면 빛 좋은 개살구와 다를 바 없다.&amp;nbsp; 디테일하게 경험을 정리하는 만큼 자소서로 추출할 때 어필해야하는 요소에 집중하고 불필요한 상황 설명, 훈훈한 마무리에 분량을 집중시킬 필요는 없다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성장과정은 호구조사나 도전정신 확인용이 아니다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;키워드에 대한 강박 없이도 자신의 철학이나 가치관을 충분히 드러낼 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;자신만의 분명한 생각과 이를 뒷받침할 수 있는 분명한 근거만 있으면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;철학과 가치관이라는 줄기에 경험이라는 살을 붙여라&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스티브 잡스는 학비 때문에 대학을 자퇴했지만 이를 최고의 결정 중 하나라고 생각한다. 흥미 없던 필수과목을 듣지 않고, 관심 있는 과목을 도강하며, 친구네서 자고, 빈병을 팔아 끼니를 때웠지만 자퇴를 했기 때문에 좋아하는 것을 좇을 수 있었다고 회고했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도강으로 배운 서체학 덕분에 매킨토시만의 서체기능을, 애플에서 해고당했지만 성공의 중압감에 벗어나 초심자로 돌아가 정말 하고 싶은 일을 찾고 세계 최초 3D 애니메이션 제작사인 픽사를 창업할 수 있었다고 회고했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자신이 좋아하는 일을 좇고, 위기가 있어도 스스로에 대한 신념과 확신을 갖고 있다면 훗날 인생의 차별점이 생긴다는 자신만의 철학을 강조했다. 또 그에 맞는 에피소드를 보여줌으로써 자신이 생각하는 중요한 가치를 설득력있게 전달한다. 이것이 성장과정 항목의 모범 정답이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성장과정 작성 시 유념해야할 부분 중 하나가 성장 '과정'이라는 것이다. 제시한 삶의 가치, 철학, 방향성을 '과정을 통해' 보여줘야 한다는 뜻이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생각보다 지난 경험을 생생하게 되돌아보는 것이 어렵다. 특히, 어떤 경험에서 내가 어떤 가치관이나 철학을 갖고 살아왔는지가 어려운 것 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;%EC%B0%B8%EA%B3%A0-1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/brunchbook/ohmsresume&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brunch.co.kr/brunchbook/ohmsresume&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>독서/스펙을 뛰어넘는 자소서</category>
      <category>옴스</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/185</guid>
      <comments>https://anythingis.tistory.com/185#entry185comment</comments>
      <pubDate>Wed, 3 Jul 2024 10:29:56 +0900</pubDate>
    </item>
    <item>
      <title>1장 취업은 나를 세일즈하는 과정이다</title>
      <link>https://anythingis.tistory.com/184</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #009a87; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;나를 팔 것인가, 못 팔 것인가?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;합격을 위해서는 '결정적인 정보'가 아닌 '나만의 결정적인 생각'이 필요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기업에서 필요할 수 밖에 없는 이유를 명확하게 설득해야함&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관련 경험이나 스펙이 아닌 나만의 가치관으로 승부한다&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스펙, '카더라'식 정보는 배제하고 회사가 해결해야하는 문제점을 근본적으로 바라보기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex) 교직원 -&amp;gt; 개인에게 업무, 책임 권한이 있어 기획부터 예산집행까지 업무 전체를 하나의 관점에서 기획하고 끌고 나갈 수 있는 역량&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부족한 점은 인정하고 본인이 갖고 있는 무기와 경쟁력을 해당 기업과 직무에 어떻게 연결시킬 수 있을 지에 대해 고민하기&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대기업 취업설명서를 충실히 따르는 취준생들&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기업에서 제시하는 가이드라인에 따르면 경쟁자와 크게 다르지 않은 스펙으로 흔한 사람이 된다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스펙쌓기도 중요하지만, 중심에는 '자신'이 있어야하고, 자신의 기준에 따라 경험을 쌓고, 자신만의 무기와 이야기를 만들어야한다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우리가 갑이고 기업이 을이다&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기업이 원하는 삶을 살지 말고, 자신이 원하는 삶을 살며 경험을 쌓아 자신만의 무기를 단련시킬 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;취업의 핵심 3요소 : 나, 회사, 직무&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;뛰어난 세일즈는 '말솜씨'가 아닌 자신이 팔아야하는 '제품'과 제품의 구매 주체인 '고객'에 대한 정확한 이해를 바탕으로 접점 찾기&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;미우나 고우나 내가 팔 제품은 나 자신이다&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;팔아야하는 대상은 자신인데 이미 팔려나간 제품의 스펙을 공부하는 바보같은 세일즈맨은 없다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;약점을 파고드는 면접관의 질문을 정면돌파하지 못하고, 자꾸만 위축되는 이유도 자신에 대한 이해와 자신감이 부족하기 때문&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;고객에 대한 진짜 이해가 있어야 세일즈 포인트가 보인다&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;누구나 쉽게 생각할 수 있는 회사, 직무 정보를 앵무새처럼 반복하거나, 주관적인 해석이나 이해 없이 인터넷에서 습득한 핵심가치, 필수역량들을 입으로 옮기는 수준으로는 고객의 마음을 얻을 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #009a87; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;세일즈의 기본은 제품인 '나'에 대한 철저한 이해다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자기가 가진 재료는 생각하지 않은 채 무작정 먹고싶고, 맛있는 레시피만 찾지 말 것&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;남의 냉장고를 뒤지는 취준생, 그리고 그 결과&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;채용설명회, 핵심역량, 인재상, 경영철학, 비전까지 조사하고 자소서를 쓰려하지만 어디서부터 어떻게 이 내용들을 풀어내야할지 멘붕이 온다. 취업은 '나를' 소개하고 판매하는 과정인데 자신과는 전혀 관련 없는 정보 수집에만 몰두했기 때문이다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;당장 시선을 내부로 돌려 우리집 내장고를 열어보자&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자기가 가진 재료로 상대방의 입맛을 사로잡을 수 있는 만반의 준비를 갖춰야한다. 자기만의 방법으로 새롭게 재료를 배합해 작은 기대조차 없었던 손님의 편견을 깨고, 음식을 흡입하게 만드는 것이 성공의 비결이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;느낀점&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;나 자신의 가치관, 많은 경험에서 무엇을 배웠는지, 어떤 일을 잘하는지에 대해 깊게 생각하지 않은 것 같다. 나를 팔기 위한 준비를 잘해보자&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;참고&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/brunchbook/ohmsresume&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brunch.co.kr/brunchbook/ohmsresume&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1719877322848&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;[브런치북] 스펙을 뛰어넘는 자소서&quot; data-og-description=&quot;취준생들이 취업시장의 왜곡된 정보에 호도되고 있습니다. 눈에 보이는 스펙과 정보들만 좇느라 외면하고 있는 취업의 본질을 바라보게 함으로써 자신만의 논리로 인사담당자를 설득할 수 있&quot; data-og-host=&quot;brunch.co.kr&quot; data-og-source-url=&quot;https://brunch.co.kr/brunchbook/ohmsresume&quot; data-og-url=&quot;https://brunch.co.kr/brunchbook/ohmsresume&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boSmxU/hyWrK1ghzT/hv5MnBT9dZnIgcUk6gxMu1/img.jpg?width=458&amp;amp;height=679&amp;amp;face=320_140_360_183,https://scrap.kakaocdn.net/dn/CM91D/hyWrSZhdG3/rO7excsAvEg0THjnJGf1Kk/img.jpg?width=500&amp;amp;height=500&amp;amp;face=352_36_396_84&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/brunchbook/ohmsresume&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://brunch.co.kr/brunchbook/ohmsresume&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boSmxU/hyWrK1ghzT/hv5MnBT9dZnIgcUk6gxMu1/img.jpg?width=458&amp;amp;height=679&amp;amp;face=320_140_360_183,https://scrap.kakaocdn.net/dn/CM91D/hyWrSZhdG3/rO7excsAvEg0THjnJGf1Kk/img.jpg?width=500&amp;amp;height=500&amp;amp;face=352_36_396_84');&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;[브런치북] 스펙을 뛰어넘는 자소서&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;brunch.co.kr&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;</description>
      <category>독서/스펙을 뛰어넘는 자소서</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/184</guid>
      <comments>https://anythingis.tistory.com/184#entry184comment</comments>
      <pubDate>Tue, 2 Jul 2024 08:42:14 +0900</pubDate>
    </item>
    <item>
      <title>SQLD 합격 후기</title>
      <link>https://anythingis.tistory.com/183</link>
      <description>&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;608&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PpPvW/btsH2FFIm4k/MOs6LJ9VdhqvTivU8w1IA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PpPvW/btsH2FFIm4k/MOs6LJ9VdhqvTivU8w1IA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PpPvW/btsH2FFIm4k/MOs6LJ9VdhqvTivU8w1IA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPpPvW%2FbtsH2FFIm4k%2FMOs6LJ9VdhqvTivU8w1IA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;283&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발을 진행하며 Mybatis와 Querydsl을 활용하면서 SQL에 대해 자신감이 있었습니다. 막상 공부해보니 SQL도 중요하지만 데이터베이스 이론적인 부분이 부족한 것 같아 유튜브의 도움을 받았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=QB_GYdHUHmA&amp;amp;list=PLbflMVhwy2jPIAzArCK90mqFlTtndFigS&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=QB_GYdHUHmA&amp;amp;list=PLbflMVhwy2jPIAzArCK90mqFlTtndFigS&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=QB_GYdHUHmA&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bxqmc8/hyWliX5HVm/9RZkjIKu39FYJy6fBsYY01/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=760_316_924_496&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;SQLD 1과목 완벽 정리 (2024년 신유형 반영) #데이터모델링 #ERD #정규화&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/QB_GYdHUHmA&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;홍쌤 강의 들으면서 전공 때 배웠던 기억과 개발하며 얻었던 경험들을 복기하면서 공부했습니다. 정처기와 달리 CBT로 풀어볼 수 있는 곳도 없어서 문제풀이보다 개념이해 위주로 공부했던 것 같습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일단 자격증은 취득했지만 책을 보면서 ORM 기술을 활용하며 까먹었던 기초에 대해서 공부하기 좋은 과목이라고 생각합니다. 아마 지속적으로 SQLD를 시험이 아닌 개발자로서 성장할 수 있도록 공부할 것 같습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <category>sqld</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/183</guid>
      <comments>https://anythingis.tistory.com/183#entry183comment</comments>
      <pubDate>Mon, 17 Jun 2024 16:09:37 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트 회고] 여의도 꿀통</title>
      <link>https://anythingis.tistory.com/179</link>
      <description>&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;4월경에 끝난 프로젝트이지만 지금이라도 프로젝트 회고를 남겨봅니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7V2ev/btsHUbFGLeL/i22WKHi0mO2zxJEWIEJdw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7V2ev/btsHUbFGLeL/i22WKHi0mO2zxJEWIEJdw0/img.png&quot; data-alt=&quot;꿀벌처럼 열심히 일하는 국회의원의 정보를 모아보는 서비스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7V2ev/btsHUbFGLeL/i22WKHi0mO2zxJEWIEJdw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7V2ev%2FbtsHUbFGLeL%2Fi22WKHi0mO2zxJEWIEJdw0%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;1280&quot; height=&quot;541&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;꿀벌처럼 열심히 일하는 국회의원의 정보를 모아보는 서비스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&quot;여의도 꿀통&quot;은 선거 후 관심도가 떨어지는 문제, 정치의 복잡성, 그리고 지지할 정당이나 정치인의 부재로 인한 무관심을 해결하기 위해 기획했습니다. 투표 이후에도 꾸준히 정치에 관심을 가질 수 있도록 돕고, 정치적 결정 과정을 보다 쉽고 이해하기 쉬운 형태로 제공함으로써, 정치에 대한 접근성을 높이고자 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;README&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자세한 기능소개, 데모영상은 리드미에 포함되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/HoneyPot&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/JaegeonYu/HoneyPot&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718080699552&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/HoneyPot: 국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스&quot; data-og-description=&quot;국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스, 여의도 꿀통. Contribute to JaegeonYu/HoneyPot development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/HoneyPot&quot; data-og-url=&quot;https://github.com/JaegeonYu/HoneyPot&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yqMij/hyWg2Vwtwt/RNZwqaxkVsSoN0QyOH1Wak/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/HoneyPot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/HoneyPot&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yqMij/hyWg2Vwtwt/RNZwqaxkVsSoN0QyOH1Wak/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/HoneyPot: 국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;국회의원의 공약이행률부터 발의법안까지 모든 정보를 제공해주는 서비스, 여의도 꿀통. Contribute to JaegeonYu/HoneyPot development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결한 문제들&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. JPA 활용한 페이지네이션, 페치조인&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;뉴스 조회 시 키워드까지 조회할 때 Querydsl 활용한 Pagination + Fetch Join 처리&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RQs2C/btsHU6Q8gw7/DPSZXKrINtRJKM2dBcHRmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RQs2C/btsHU6Q8gw7/DPSZXKrINtRJKM2dBcHRmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RQs2C/btsHU6Q8gw7/DPSZXKrINtRJKM2dBcHRmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRQs2C%2FbtsHU6Q8gw7%2FDPSZXKrINtRJKM2dBcHRmK%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;854&quot; height=&quot;481&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 후보자 정보 PDF&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3MB이상의 후보자 정보 PDF를 S3를 활용해 제공할 때 5초 이상의 네트워크 지연시간이 소요되었는데, CDN 기능이 적용된 CloudFront + S3를 통해 1초 이내로 줄일 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 후보자 사진 해상도 개선&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;국회 및 공공 데이터 포탈에서 제공하는 국회의원의 해상도가 제각각이여서 OpenCV의 dnn super resolution 모델을 활용해 이미지 업스케일링을 진행했습니다. 인위적인 느낌이 나긴하지만 서비스 내 썸네일로 보여지는 부분이라 높은 해상도를 챙기기로 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SG581/btsHVSR9ndd/LKMmNKLch3lUJ1q8FkOPL0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SG581/btsHVSR9ndd/LKMmNKLch3lUJ1q8FkOPL0/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;160&quot; data-origin-height=&quot;224&quot; data-filename=&quot;9771303 (1).jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SG581/btsHVSR9ndd/LKMmNKLch3lUJ1q8FkOPL0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSG581%2FbtsHVSR9ndd%2FLKMmNKLch3lUJ1q8FkOPL0%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;160&quot; height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yEObn/btsHUnF41Gq/EKKW9KNCPs7ORhAqM9aE20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yEObn/btsHUnF41Gq/EKKW9KNCPs7ORhAqM9aE20/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;672&quot; data-filename=&quot;9771303.jpg&quot; width=&quot;143&quot; height=&quot;200&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yEObn/btsHUnF41Gq/EKKW9KNCPs7ORhAqM9aE20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyEObn%2FbtsHUnF41Gq%2FEKKW9KNCPs7ORhAqM9aE20%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;480&quot; height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;처음엔 업스케일링 해주는 사이트에서 4~5개씩 집어넣고 다운 받아 진행했는데 인공지능 전공 수업을 듣던 기억을 되살리며 구글링과 Colab을 활용해 300개 이상의 사진 업스케일링을 자동화할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;977&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HSebw/btsHVs7xj5W/kTmql1ukXmrHlNDyeUDbj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HSebw/btsHVs7xj5W/kTmql1ukXmrHlNDyeUDbj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HSebw/btsHVs7xj5W/kTmql1ukXmrHlNDyeUDbj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHSebw%2FbtsHVs7xj5W%2FkTmql1ukXmrHlNDyeUDbj0%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;977&quot; height=&quot;561&quot; data-origin-width=&quot;977&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; 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;825&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRtrWC/btsHVFetJRt/lWqEGgRz3Gaq0x7w3MwoF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRtrWC/btsHVFetJRt/lWqEGgRz3Gaq0x7w3MwoF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRtrWC/btsHVFetJRt/lWqEGgRz3Gaq0x7w3MwoF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRtrWC%2FbtsHVFetJRt%2FlWqEGgRz3Gaq0x7w3MwoF0%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;825&quot; height=&quot;704&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;704&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성과&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 800명의 사용자 활동을 수용한 UX 개선&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efXrON/btsHU82rAXj/SyhGVAAEBouVNFIbVqJ3y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efXrON/btsHU82rAXj/SyhGVAAEBouVNFIbVqJ3y1/img.png&quot; data-alt=&quot;Next.JS 활용한 SEO 최적화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efXrON/btsHU82rAXj/SyhGVAAEBouVNFIbVqJ3y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefXrON%2FbtsHU82rAXj%2FSyhGVAAEBouVNFIbVqJ3y1%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;1150&quot; height=&quot;572&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.JS 활용한 SEO 최적화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HpDOp/btsHUdwLos8/rRpO2WrWfLSeJSvBmtN3uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HpDOp/btsHUdwLos8/rRpO2WrWfLSeJSvBmtN3uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HpDOp/btsHUdwLos8/rRpO2WrWfLSeJSvBmtN3uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHpDOp%2FbtsHUdwLos8%2FrRpO2WrWfLSeJSvBmtN3uk%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;1280&quot; height=&quot;585&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그동안 많은 프로젝트를 배포해봤지만 실제 사용자를 받아본적은 없었습니다. 이번 프로젝트는 &lt;i&gt;&lt;b&gt;대선 시즌을 노린 실제 사용자&lt;/b&gt;&lt;/i&gt;, 그리고 선거법 위반을 피하기 위해 &lt;b&gt;&lt;i&gt;유저 기능, 커뮤니티 기능을 제외&lt;/i&gt;&lt;/b&gt;하고 &lt;i&gt;&lt;b&gt;능동적인 컨텐츠 제공&lt;/b&gt;&lt;/i&gt;을 위해 노력했습니다. 한국매니페스토본부가 제공하는 공약이행현황 hwp 파일, 국회에서 제공하는 국회의원과 법안 정보, 유튜브 국회뉴스를 java, python, GPT API를 활용해 파싱 및 가공해 제공했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;삼성 청년 SW 아카데미 내부 메신저 및 외부 홍보를 통해 &lt;b&gt;&lt;i&gt;800명 이상의 사용자&lt;/i&gt;&lt;/b&gt;를 받아볼 수 있었습니다.&amp;nbsp;Google Analytics와 Beusable Hitmap을 활용해 사용자와 이벤트를 추적하고 &lt;i&gt;&lt;b&gt;많은 이벤트가 발생한 페이지&lt;/b&gt;&lt;/i&gt;에서 컴포넌트별 클릭 및 스크롤 히트맵을 통해 얻은 피드백으로 &lt;i&gt;&lt;b&gt;카테고리 순서 재배치 및 이동 버튼&lt;/b&gt;&lt;/i&gt; 등을 추가했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 프로젝트 우수상&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;삼성 청년 SW 아카데미 특화 프로젝트 우수상 수상했습니다.&amp;nbsp; 더 원대한 꿈을 꿨었는데 아쉬웠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;회고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이번 팀 프로젝트도 6인 규모에 6주동안 진행되었고 팀장, 백엔드, 네트워크 인프라를 담당했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획때부터 해당 아이디어에 팀원 모두 관심이 있었지만 능동적인 콘텐츠를 제공하기에 AI로 국회의원이나 후보자가 추구하는 방향성을 예측해보고 싶었지만 선거법 관련해서 객관적인 정보 취합 후 알아보기 쉽게 제공하는 쪽으로 갔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자동화의 필요성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;한국매니페스토 본부에서 각 의원실에 질의한 공약이행 현황 한글 파일을 파싱하는 부분을 Java를 통해 자동화를 해놔서 22년 기준 데이터에서 프로젝트 종료 4일전에 올라온 24년 데이터로 마이그레이션을 진행할 수 있었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;300명의 현직 국회의원 사진 정보도 화질이 깨져 서비스가 지장가는 데이터가 많이 있었는데 python, colab을 활용해 이미지 업스케일링을 진행할 수 있었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;배포자동화 뿐만 아니라 단순한 노동을 자동화하면서 얻는 이점이 크고, 예외처리의 중요성을 다시 한번 깨달을 수 있던 프로젝트였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;실제 사용자&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;삼성 청년 SW 아카데미를 진행하면서 전 지역의 교육생들에게 홍보할 수 있는 네트워크가 하나의 장점이라고 생각합니다. 제 기수와 친구가 속한 그 다음 기수 교육생들에게 내부 메신저 홍보를 통해 800명 이상의 트래픽을 경험해볼 수 있었습니다. 프론트엔드에 GA, Hitmap을 통해 트래픽을 모니터링했지만 백엔드에 Grafana, Scouter를 못붙여봐서 화면이 아닌 어떤 데이터에 많이 접근하는지는 체크를 하지 못했습니다. 이 부분을 공부해서 성능상에 향상이 필요하거나 문제가 있는 부분을 찾아볼 것 같습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/179</guid>
      <comments>https://anythingis.tistory.com/179#entry179comment</comments>
      <pubDate>Tue, 11 Jun 2024 15:00:26 +0900</pubDate>
    </item>
    <item>
      <title>Java(Spring)를 익힌 이유</title>
      <link>https://anythingis.tistory.com/178</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;최근 SSAFY에서 프로젝트 회고를 하며 Jenkins, Nginx 등 특정 기술을 사용한 이유를 정리할 기회가 있었습니다. 그러다 MySQL, Spring을 거슬러 웹 개발을 위한 언어로 JAVA를 선택한 이유를 떠올려봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCmKcM/btsGspyx9vC/ycZNEkkeRwJtBheT6AfVzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCmKcM/btsGspyx9vC/ycZNEkkeRwJtBheT6AfVzK/img.png&quot; data-alt=&quot;22년 12월 회고록 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCmKcM/btsGspyx9vC/ycZNEkkeRwJtBheT6AfVzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCmKcM%2FbtsGspyx9vC%2FycZNEkkeRwJtBheT6AfVzK%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;869&quot; height=&quot;268&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;22년 12월 회고록 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;졸업프로젝트를 하며 비즈니스 로직 설계하는 백엔드에 흥미를 느꼈지만, 처음 접한 자바스크립트 + Express.js보다는 Java + Spring을 택했습니다. 전공자로서 자바 프로그래밍, 네트워크 프로그래밍 등의 수업에서 자연스럽게 노출되었고 대한민국 백엔드풀은 자바가 압도적이라 선택했습니다만 시장에서 이런 점유율을 차지할 수 있던 이유는 &lt;i&gt;&lt;b&gt;개발자가 서비스 자체에 집중&lt;/b&gt;&lt;/i&gt;해줄 수 있게 해줘서 인것 같습니다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: left;&quot;&gt;1. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: left;&quot;&gt;Write once, run anywhere를 위한 JVM으로 운영체제에 대한 고민&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: left;&quot;&gt;2. GC(garbage collector), 포인터 X&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: left;&quot;&gt;자율성은 떨어지지만, 안전성을 올리는 프레임워크같은 기능을 통해 클래스, 비즈니스 로직을&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: left;&quot;&gt;&amp;nbsp;설계하는데에 집중할 수 있게해줘 전자정부프레임워크, SpringBoot를 많은 회사들이 채택하지 않을까 생각했습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <category>java</category>
      <category>spring</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/178</guid>
      <comments>https://anythingis.tistory.com/178#entry178comment</comments>
      <pubDate>Mon, 8 Apr 2024 21:58:21 +0900</pubDate>
    </item>
    <item>
      <title>[인프라] EC2, docker 이용한 배포환경 기초</title>
      <link>https://anythingis.tistory.com/176</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도커는 애플리케이션 계층의 추상화, 커널을 공유함(운영체제 단위의 포트 공유하기에 같은 번호 포트 사용은 안됨)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;spring, db,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. docker run jar&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQHmMh/btsF8mWokH1/XFKEQjtbeykiGmR9ELoSVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQHmMh/btsF8mWokH1/XFKEQjtbeykiGmR9ELoSVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQHmMh/btsF8mWokH1/XFKEQjtbeykiGmR9ELoSVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQHmMh%2FbtsF8mWokH1%2FXFKEQjtbeykiGmR9ELoSVK%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;741&quot; height=&quot;525&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. docker run -p 8080:8080 jar&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bofV64/btsF9DQOvbo/8aDc7CmBmFsrhK5fx3mf9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bofV64/btsF9DQOvbo/8aDc7CmBmFsrhK5fx3mf9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bofV64/btsF9DQOvbo/8aDc7CmBmFsrhK5fx3mf9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbofV64%2FbtsF9DQOvbo%2F8aDc7CmBmFsrhK5fx3mf9k%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;734&quot; height=&quot;520&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. docker run -p 8080:8080 jar &amp;amp; EC2 보안 설정 인바운딩 8080 허용&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3SP2y/btsF9bNOh1X/01SE9SjGuDud4eyaCoirdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3SP2y/btsF9bNOh1X/01SE9SjGuDud4eyaCoirdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3SP2y/btsF9bNOh1X/01SE9SjGuDud4eyaCoirdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3SP2y%2FbtsF9bNOh1X%2F01SE9SjGuDud4eyaCoirdk%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;872&quot; height=&quot;533&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. docker run -p 8080:8080 jar &amp;amp; EC2 보안 설정 인바운딩 8080 허용 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;amp;&amp;amp; docker run -p 33006:3306 db &amp;amp; EC2 보안 설정 인바운딩 3306 허용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;db.url = EC2 IP&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;db.port = 3306&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV8fLN/btsF7KDv9VX/tm7Z9Ksn5BKtINhUrj0Zj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV8fLN/btsF7KDv9VX/tm7Z9Ksn5BKtINhUrj0Zj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV8fLN/btsF7KDv9VX/tm7Z9Ksn5BKtINhUrj0Zj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbV8fLN%2FbtsF7KDv9VX%2Ftm7Z9Ksn5BKtINhUrj0Zj1%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;1372&quot; height=&quot;689&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5.&amp;nbsp;docker run -p 8080:8080 jar &amp;amp; EC2 보안 설정 인바운딩 8080 허용&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;amp;&amp;amp; docker run -p 33006:3306 db&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.yml&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;db.url = docker network db IP&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;db.port = 3306&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLORto/btsGaDbxusu/em9Qnf6o16ja36V6ASyle1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLORto/btsGaDbxusu/em9Qnf6o16ja36V6ASyle1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLORto/btsGaDbxusu/em9Qnf6o16ja36V6ASyle1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLORto%2FbtsGaDbxusu%2Fem9Qnf6o16ja36V6ASyle1%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;1356&quot; height=&quot;691&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;691&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배포환경은 최대한 외부와 접속을 차단하는 방향으로 설계&lt;/span&gt;&lt;/p&gt;</description>
      <category>인프라</category>
      <category>docker</category>
      <category>EC2</category>
      <category>인프라</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/176</guid>
      <comments>https://anythingis.tistory.com/176#entry176comment</comments>
      <pubDate>Thu, 28 Mar 2024 08:42:56 +0900</pubDate>
    </item>
    <item>
      <title>프록시 서버, Nginx 설정(리버스 프록시, SSL)(Ubuntu 24.03ver)</title>
      <link>https://anythingis.tistory.com/175</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프록시 서버 왜 쓸까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본 웹 서비스 통신의 방식입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1077&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gki9U/btsF3FN4piN/Qf6xfWxit6RewLCqEwk9H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gki9U/btsF3FN4piN/Qf6xfWxit6RewLCqEwk9H1/img.png&quot; data-alt=&quot;프론트엔드에게 화면요청&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gki9U/btsF3FN4piN/Qf6xfWxit6RewLCqEwk9H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGki9U%2FbtsF3FN4piN%2FQf6xfWxit6RewLCqEwk9H1%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;1077&quot; height=&quot;515&quot; data-origin-width=&quot;1077&quot; data-origin-height=&quot;515&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프론트엔드에게 화면요청&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;www.naver.com을 치면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1725&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/esPBDO/btsF18jjEcG/SkkCRPlk7TnJFbCJy3VLrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/esPBDO/btsF18jjEcG/SkkCRPlk7TnJFbCJy3VLrk/img.png&quot; data-alt=&quot;요런 화면 제공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/esPBDO/btsF18jjEcG/SkkCRPlk7TnJFbCJy3VLrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FesPBDO%2FbtsF18jjEcG%2FSkkCRPlk7TnJFbCJy3VLrk%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;1725&quot; height=&quot;290&quot; data-origin-width=&quot;1725&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요런 화면 제공&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런 화면을 받아오는 것과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yInMv/btsF2wqK5zJ/xV8tI4CfFWgYrCcsL9XecK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yInMv/btsF2wqK5zJ/xV8tI4CfFWgYrCcsL9XecK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yInMv/btsF2wqK5zJ/xV8tI4CfFWgYrCcsL9XecK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyInMv%2FbtsF2wqK5zJ%2FxV8tI4CfFWgYrCcsL9XecK%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;1075&quot; height=&quot;516&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;516&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후 로그인을 하는 등 비즈니스 로직과 관련된 부분을 동작하면 백엔드에 요청하고 응답받은 리소스를 활용해 화면을 재구성합니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;539&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSbBPw/btsF1RPBUtt/BqHp65jih99g65wfHEPsf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSbBPw/btsF1RPBUtt/BqHp65jih99g65wfHEPsf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSbBPw/btsF1RPBUtt/BqHp65jih99g65wfHEPsf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSbBPw%2FbtsF1RPBUtt%2FBqHp65jih99g65wfHEPsf0%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;1057&quot; height=&quot;539&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;539&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;1755&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLEdkf/btsF1I6i2tc/NQJ4I01HfEOlUSkPGruTn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLEdkf/btsF1I6i2tc/NQJ4I01HfEOlUSkPGruTn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLEdkf/btsF1I6i2tc/NQJ4I01HfEOlUSkPGruTn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLEdkf%2FbtsF1I6i2tc%2FNQJ4I01HfEOlUSkPGruTn0%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;1755&quot; height=&quot;545&quot; data-origin-width=&quot;1755&quot; data-origin-height=&quot;545&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;웹 서비스 인프라를 구축하며 서버의 프론트엔드의 3000번과 백엔드의 8080번 포트를 모두 직접 노출을 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 그 앞단에&amp;nbsp; 프록시 서버를 두면 아래와 같이 통신하고 다음과 같은 장점들을 갖습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lOK73/btsF1lRiy2z/QmSwfrXqFiU6uqSC9mW3Ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lOK73/btsF1lRiy2z/QmSwfrXqFiU6uqSC9mW3Ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lOK73/btsF1lRiy2z/QmSwfrXqFiU6uqSC9mW3Ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlOK73%2FbtsF1lRiy2z%2FQmSwfrXqFiU6uqSC9mW3Ck%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;1268&quot; height=&quot;515&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;515&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx를 &lt;i&gt;&lt;b&gt;프록시 서버&lt;/b&gt;&lt;/i&gt;로 활용해 아래와 같은 이점을 살렸습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 리버스 프록시 기능으로 백엔드, 프론트엔드의 포트노출을 최소화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. SSL 오프로딩으로 하위 백엔드 서버와 프론트엔드 서버는 http 통신만으로 부하를 줄이고 인증서 유지보수 간소화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. Upstream 기능으로 부하 분산 및 백업 서버 관리&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx 왜 쓸까?&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2lABl/btsF1IrJlFx/fCRoPvdRJnSPzO03xTPRYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2lABl/btsF1IrJlFx/fCRoPvdRJnSPzO03xTPRYk/img.png&quot; data-alt=&quot;가끔가다 볼 수 있는 오류 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2lABl/btsF1IrJlFx/fCRoPvdRJnSPzO03xTPRYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2lABl%2FbtsF1IrJlFx%2FfCRoPvdRJnSPzO03xTPRYk%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;508&quot; height=&quot;187&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가끔가다 볼 수 있는 오류 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;높은 성능&lt;/b&gt;: Nginx는 이벤트 기반 아키텍처를 사용하여 매우 높은 성능을 제공합니다. 비동기 처리 방식을 통해 동시에 많은 연결을 처리할 수 있으며, 효율적인 리소스 관리를 통해 빠른 응답 시간을 보장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;저 메모리 사용량&lt;/b&gt;: Nginx는 경량화된 설계로 메모리 사용량이 상대적으로 낮습니다. 이는 더 많은 연결을 처리하면서도 시스템 리소스를 적게 사용하여 효율성을 높입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;확장성&lt;/b&gt;: Nginx는 수평 및 수직 확장이 가능합니다. 단일 서버 또는 다중 서버 환경에서도 사용할 수 있으며, 로드 밸런싱 및 부하 분산 기능을 통해 트래픽을 효율적으로 관리할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx는 비교적 가볍고 강력한 성능을 제공하고, 무엇보다 설정이 직관적이고 간단합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://prohannah.tistory.com/136&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://prohannah.tistory.com/136&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711260345046&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;Nginx 기본 환경 설정&quot; data-og-description=&quot;Nginx 기본 환경 설정 Nginx는 환경 설정 텍스트 파일로 여러 가지 값을 지정해 Nginx 설정을 할 수 있도록 지원한다. Nginx 설치 시 기본적으로 설정하는 환경설정 값들을 알아보겠다. 참고 링크 아래 &quot; data-og-host=&quot;prohannah.tistory.com&quot; data-og-source-url=&quot;https://prohannah.tistory.com/136&quot; data-og-url=&quot;https://prohannah.tistory.com/136&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c6D2DY/hyVDCbdjMQ/yD0S3VVC2EBWr57jaQNytK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/zxtri/hyVDys7lv2/89IBBISYKJiPQfGJYmXHP0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://prohannah.tistory.com/136&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://prohannah.tistory.com/136&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c6D2DY/hyVDCbdjMQ/yD0S3VVC2EBWr57jaQNytK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/zxtri/hyVDys7lv2/89IBBISYKJiPQfGJYmXHP0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;Nginx 기본 환경 설정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nginx 기본 환경 설정 Nginx는 환경 설정 텍스트 파일로 여러 가지 값을 지정해 Nginx 설정을 할 수 있도록 지원한다. Nginx 설치 시 기본적으로 설정하는 환경설정 값들을 알아보겠다. 참고 링크 아래&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;prohannah.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저는 &lt;i&gt;&lt;b&gt;가상 호스팅 개념을 위한 Server블록&lt;/b&gt;&lt;/i&gt;과 Server 블록 내에 &lt;i&gt;&lt;b&gt;특정 URL 처리를 위한 Location블록&lt;/b&gt;&lt;/i&gt;을 사용했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx 설정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음은 위의 설정을 어떻게 관리하나? 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;먼저 /etc/nginx/.nginx.conf 파일입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메인 설정 파일이고 include로 /etc/nginx/conf.d/*.conf 파일들과 /etc/nginx/sites-enabled/* 파일들을 읽어 적용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711261246175&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

		##
        # SSL Settings // SSL 설정
        ##
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;
		
        ##
        # Logging Settings // 로그 파일 저장 위치
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        ##
        # Gzip Settings 
        ##
        
        gzip on;
		
        ##
        # Virtual Host Configs // 하위 설정파일 읽기 (sites-enabled)
        ##
        
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;conf.d 하위 파일은 &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;전역 설정 및 모듈 설정을,&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sites-enabled 하위 파일은&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;특정 도메인 또는 가상 호스트 설정을 맡습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;또한, &lt;/span&gt;sites-enabled&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; 디렉토리에서 가상 호스트 설정을 활성화하거나 비활성화하는 것만으로도 특정 웹 사이트를 쉽게 추가하거나 제거할 수 있습니다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBKYb9/btsF13vCJjz/5omCmFngrMo0e62bqKdfP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBKYb9/btsF13vCJjz/5omCmFngrMo0e62bqKdfP0/img.png&quot; data-alt=&quot;요런 느낌&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBKYb9/btsF13vCJjz/5omCmFngrMo0e62bqKdfP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBKYb9%2FbtsF13vCJjz%2F5omCmFngrMo0e62bqKdfP0%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;1548&quot; height=&quot;516&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요런 느낌&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Reverse Proxy, SSL 적용(Ubuntu Linux)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1. nginx 설치&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711262027400&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;2. 방화벽 적용&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711262114959&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo ufw app list

Nginx HTTP: 포트 80번만 허용
Nginx HTTPS: 포트 443번만 허용
Nginx Full: 포트 80, 443번 허용

sudo ufw allow 'Nginx Full'

sudo ufw status

Status: active

To                         Action      From
--                         ------      ----
Nginx HTTP                 ALLOW       Anywhere
Nginx HTTP (v6)            ALLOW       Anywhere (v6)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;3. Certbot 설치(&lt;span style=&quot;color: #ee2323;&quot;&gt;!! PPA 이용 방식은 최근 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Deprecate 되었습니다.&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711262352229&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apt-get install python3-certbot-nginx

certbot certonly --nginx -d example.com


경로확인
/etc/letsencrypt/live/example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;4. sites-available 디렉토리 밑에 하위 설정 파일 생성&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;위의 example.com -&amp;gt; [domain name]으로 대체하시면 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711261931102&quot; class=&quot;jboss-cli&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;sites-available/[domainname] 

server { // https프로토콜(443포트) 처리 블록
    server_name [domain name];
    listen 443 ssl;

     ssl_certificate /etc/letsencrypt/live/[domain name]/fullchain.pem; // SSL 인증서
     ssl_certificate_key /etc/letsencrypt/live/[domain name]/privkey.pem; // SSL 인증서

    location /api { // api로 시작하는 요청은 백엔드로
        proxy_pass http://localhost:8080;
        proxy_set_header X-Real-IP $remote_addr;
        error_page 405 = $uri;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Origin http://localhost:3000; // CORS 에러처리
        proxy_set_header Authorization $http_authorization;

    }

     location / { // 그 외의 요청은 프론트엔드로
        proxy_pass http://localhost:3000;
}


}
server { // http프로토콜(80포트) 처리 블록
    if ($host = [domain name]) { // https로 redirect
        return 301 https://$host$request_uri;
    } # managed by Certbot

  listen 80;
  server_name [domain name];
    return 404; # managed by Certbot
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5. sites-enabled 심볼릭 링크&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711262591564&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;심볼릭 링크
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com

구동 확인 syntax is ok, test is successful이 출력되야 합니다.
nginx -t

설정 적용
nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;짜잔, SSL과 리버스 프록시 적용이 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;리버스 프록시 서버(포트 노출 줄여 보안강화, server &amp;amp; location block)&lt;/b&gt;&lt;/i&gt;를 적용하는데 &lt;b&gt;&lt;i&gt;nginx 웹서버&lt;/i&gt;&lt;/b&gt;를 활용한 이유(경량, 이벤트 아키텍처 &amp;amp; 비동기로 높은 성능),&lt;b&gt; nginx 설정 방법(sites-available)&lt;/b&gt;과 간단한 &lt;i&gt;&lt;b&gt;SSL 인증서&lt;/b&gt;&lt;/i&gt; 적용까지 정리해봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://nginx.org/en/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nginx.org/en/docs/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711263014220&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;nginx documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;nginx.org&quot; data-og-source-url=&quot;https://nginx.org/en/docs/&quot; data-og-url=&quot;https://nginx.org/en/docs/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://nginx.org/en/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nginx.org/en/docs/&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;nginx documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nginx.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://jaehyeon48.github.io/nginx/configure-nginx-on-ubuntu-2004/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jaehyeon48.github.io/nginx/configure-nginx-on-ubuntu-2004/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711262997457&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;Ubuntu 20.04에서 Nginx 설치 및 설정하기&quot; data-og-description=&quot;이 포스트는 프로젝트를 배포하는 과정에서 참고한 여러 개의 포스트를 종합한 글입니다. Ubuntu 20.04에 Nginx 설치하기 Nginx 설치 방화벽 설정 위 명령을 실행하면 아래와 같은 리스트가 출력됩니&quot; data-og-host=&quot;jaehyeon48.github.io&quot; data-og-source-url=&quot;https://jaehyeon48.github.io/nginx/configure-nginx-on-ubuntu-2004/&quot; data-og-url=&quot;https://jaehyeon48.github.io/nginx/configure-nginx-on-ubuntu-2004/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BwPL2/hyVDGSdL6B/u5ygoOVVJBPKswzlqqUkd1/img.png?width=640&amp;amp;height=243&amp;amp;face=0_0_640_243&quot;&gt;&lt;a href=&quot;https://jaehyeon48.github.io/nginx/configure-nginx-on-ubuntu-2004/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jaehyeon48.github.io/nginx/configure-nginx-on-ubuntu-2004/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BwPL2/hyVDGSdL6B/u5ygoOVVJBPKswzlqqUkd1/img.png?width=640&amp;amp;height=243&amp;amp;face=0_0_640_243');&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;Ubuntu 20.04에서 Nginx 설치 및 설정하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 포스트는 프로젝트를 배포하는 과정에서 참고한 여러 개의 포스트를 종합한 글입니다. Ubuntu 20.04에 Nginx 설치하기 Nginx 설치 방화벽 설정 위 명령을 실행하면 아래와 같은 리스트가 출력됩니&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jaehyeon48.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://prohannah.tistory.com/136&quot;&gt;https://prohannah.tistory.com/136&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711262999367&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;Nginx 기본 환경 설정&quot; data-og-description=&quot;Nginx 기본 환경 설정 Nginx는 환경 설정 텍스트 파일로 여러 가지 값을 지정해 Nginx 설정을 할 수 있도록 지원한다. Nginx 설치 시 기본적으로 설정하는 환경설정 값들을 알아보겠다. 참고 링크 아래 &quot; data-og-host=&quot;prohannah.tistory.com&quot; data-og-source-url=&quot;https://prohannah.tistory.com/136&quot; data-og-url=&quot;https://prohannah.tistory.com/136&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c6D2DY/hyVDCbdjMQ/yD0S3VVC2EBWr57jaQNytK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/zxtri/hyVDys7lv2/89IBBISYKJiPQfGJYmXHP0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://prohannah.tistory.com/136&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://prohannah.tistory.com/136&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c6D2DY/hyVDCbdjMQ/yD0S3VVC2EBWr57jaQNytK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/zxtri/hyVDys7lv2/89IBBISYKJiPQfGJYmXHP0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;Nginx 기본 환경 설정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nginx 기본 환경 설정 Nginx는 환경 설정 텍스트 파일로 여러 가지 값을 지정해 Nginx 설정을 할 수 있도록 지원한다. Nginx 설치 시 기본적으로 설정하는 환경설정 값들을 알아보겠다. 참고 링크 아래&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;prohannah.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://velog.io/@coastby/Nginx-SSL-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@coastby/Nginx-SSL-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711263027666&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;[Nginx] SSL 적용하기&quot; data-og-description=&quot;SSL(Secure Socket Layer) 또는 TLS(Transport Layer Security)은 전송계층 상에서 클라이언트, 서버에 대한 인증 및 데이터 암호화를 수행한다. 클라이언트와 서버 양단 간 응용계층 및 TCP 전송계층 사이에서 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@coastby/Nginx-SSL-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@coastby/Nginx-SSL-적용하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lGJ0Y/hyVDFZ5KXp/5k46MNaaPKXvfSnJIzk5I0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/XiX0q/hyVDC97AAI/KKzGm0lWc9GeUrSEdS0R0K/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200&quot;&gt;&lt;a href=&quot;https://velog.io/@coastby/Nginx-SSL-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@coastby/Nginx-SSL-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lGJ0Y/hyVDFZ5KXp/5k46MNaaPKXvfSnJIzk5I0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/XiX0q/hyVDC97AAI/KKzGm0lWc9GeUrSEdS0R0K/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200');&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;[Nginx] SSL 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSL(Secure Socket Layer) 또는 TLS(Transport Layer Security)은 전송계층 상에서 클라이언트, 서버에 대한 인증 및 데이터 암호화를 수행한다. 클라이언트와 서버 양단 간 응용계층 및 TCP 전송계층 사이에서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://blog.naver.com/pjt3591oo/222242046633a&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/pjt3591oo/222242046633a&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711263044995&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;[nginx] nginx 시작하기 1/3 - 기초편&quot; data-og-description=&quot;안녕하세요 멍개입니다. 이번시간엔 nginx를 다뤄보도록 하겠습니다. ● 설치 여기서는 ubuntu 환경에서 설...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://blog.naver.com/pjt3591oo/222242046633a&quot; data-og-url=&quot;https://blog.naver.com/pjt3591oo/222242046633&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iZCfj/hyVDEfNiWG/cK68hAyNyfXHIovKpw6XT0/img.png?width=743&amp;amp;height=517&amp;amp;face=0_0_743_517&quot;&gt;&lt;a href=&quot;https://blog.naver.com/pjt3591oo/222242046633a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.naver.com/pjt3591oo/222242046633a&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iZCfj/hyVDEfNiWG/cK68hAyNyfXHIovKpw6XT0/img.png?width=743&amp;amp;height=517&amp;amp;face=0_0_743_517');&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;[nginx] nginx 시작하기 1/3 - 기초편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요 멍개입니다. 이번시간엔 nginx를 다뤄보도록 하겠습니다. ● 설치 여기서는 ubuntu 환경에서 설...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.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;</description>
      <category>인프라</category>
      <category>certbot</category>
      <category>Nginx</category>
      <category>Reverse Proxy</category>
      <category>SSL</category>
      <category>ubuntu</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/175</guid>
      <comments>https://anythingis.tistory.com/175#entry175comment</comments>
      <pubDate>Sun, 24 Mar 2024 15:51:15 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트 회고] 시작 프로젝트 회고</title>
      <link>https://anythingis.tistory.com/174</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708515681643&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - kanggeonnim/PerspectiView: 시점으로 보는 작품, 시작&quot; data-og-description=&quot;시점으로 보는 작품, 시작. Contribute to kanggeonnim/PerspectiView development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file&quot; data-og-url=&quot;https://github.com/kanggeonnim/PerspectiView&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/kanggeonnim/PerspectiView?tab=readme-ov-file&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;GitHub - kanggeonnim/PerspectiView: 시점으로 보는 작품, 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;시점으로 보는 작품, 시작. Contribute to kanggeonnim/PerspectiView development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전까지 개인 토이프로젝트, 2인 프로젝트는 경험해봤지만 6인 팀프로젝트와 팀장은 특히 더 무거웠습니다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자세한 기획과 기능 데모는 깃허브를 통해 확인하실 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트에서 제가 맡았던 부분 위주로 회고 시작하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저는 평소에 &lt;i&gt;&lt;b&gt;웹툰, 웹소설을 즐겨보는 독자&lt;/b&gt;&lt;/i&gt;지만 &lt;i&gt;&lt;b&gt;작품 설정 관리가 안되는 작품&lt;/b&gt;&lt;/i&gt;들은 재미가 없어 손이 잘 안가게 되고, 이런 부분에서 작가분들에게 &lt;i&gt;&lt;b&gt;시각적으로 작품설정을 관리하는 서비스&lt;/b&gt;&lt;/i&gt;가 있으면 도움이 될 것 같다고 생각해 프로젝트 기획 및 구현을 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사전 인터뷰&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획은 했지만 실제 웹툰, 웹소설 스토리 작가분들에게 도움이 될만한 서비스인지 &lt;i&gt;&lt;b&gt;네이버 웹툰 작가분과 대면 인터뷰&lt;/b&gt;&lt;/i&gt;, 레진 코믹스와 카카오페이지의 작가 분과 이메일 인터뷰를 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMVy3z/btsE8CM69Xl/jOYSffvO83NNJdQfxVcTn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMVy3z/btsE8CM69Xl/jOYSffvO83NNJdQfxVcTn1/img.png&quot; data-alt=&quot;샤니 작가님과 인터뷰&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMVy3z/btsE8CM69Xl/jOYSffvO83NNJdQfxVcTn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMVy3z%2FbtsE8CM69Xl%2FjOYSffvO83NNJdQfxVcTn1%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;1286&quot; height=&quot;723&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;샤니 작가님과 인터뷰&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SpgBw/btsE99wOdhI/o9HcEo7qKXdrLWkCJCWynk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SpgBw/btsE99wOdhI/o9HcEo7qKXdrLWkCJCWynk/img.png&quot; data-alt=&quot;기획한 서비스의 필요성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SpgBw/btsE99wOdhI/o9HcEo7qKXdrLWkCJCWynk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSpgBw%2FbtsE99wOdhI%2Fo9HcEo7qKXdrLWkCJCWynk%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;1282&quot; height=&quot;723&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기획한 서비스의 필요성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y6DLV/btsE9Pk0dc9/GX6urOcKUqTxu53lrzzBPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y6DLV/btsE9Pk0dc9/GX6urOcKUqTxu53lrzzBPK/img.png&quot; data-alt=&quot;부가적으로 있으면 좋을만한 기능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y6DLV/btsE9Pk0dc9/GX6urOcKUqTxu53lrzzBPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY6DLV%2FbtsE9Pk0dc9%2FGX6urOcKUqTxu53lrzzBPK%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;1291&quot; height=&quot;710&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;부가적으로 있으면 좋을만한 기능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존에 스토리를 작성하며 독자가 있는 서비스를 생각했지만 작가분들과 인터뷰를 통해 아이디어를 남에게 노출하는 것을 꺼리고 차라리 작가 팀과의 협업이 가능한 &lt;i&gt;&lt;b&gt;팀&lt;/b&gt;&lt;b&gt; 단위 서비스의 방향성 피드백&lt;/b&gt;&lt;/i&gt;과 &lt;i&gt;&lt;b&gt;부가적으로 있으면 좋을 기능&lt;/b&gt;&lt;/i&gt;들의 디테일한 피드백을 통해 기획을 마무리 했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; 쉬는 주말에 혼자 대전에서 포항까지 인터뷰를 하느라 힘들었지만 피드백을 통해 더 나은 방향성과 디테일들을 챙길 수 있어 보람을 느꼈습니다. &lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;6명이 1팀?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사공이 많으면 배가 산으로 간다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;진짜였습니다. 매일 9시와 5시 30분에 스크럼을 진행했지만 모든 팀원이 한 방향을 향해 달려가는지 체크하는 것이 생각보다 힘이 들었습니다. 팀장으로서 임무 분배를 한 것이 잘 진행되고 있는지, 오늘이나 이번주에 완료될 진행상황들을 잘 보고해주는 팀원이 고맙고, 한주 한주 지나갈 수록 모든 팀원과 소통이 잘되어 초반보다 원활한 임무분할을 할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JIRA&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JIRA를 통해 1주 단위 스프린트를 위한 에픽(목표)과 에픽 달성을 위한 세분화된 스토리 포인트 분배 및 스토리 관리 덕에 성공적으로 프로젝트를 완료할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7SAT7/btsFbTfLJ57/8vu1M3yK2xfniIVf20KS4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7SAT7/btsFbTfLJ57/8vu1M3yK2xfniIVf20KS4k/img.png&quot; data-alt=&quot;번다운 차트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7SAT7/btsFbTfLJ57/8vu1M3yK2xfniIVf20KS4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7SAT7%2FbtsFbTfLJ57%2F8vu1M3yK2xfniIVf20KS4k%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;1584&quot; height=&quot;695&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;번다운 차트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnvGhp/btsFck49c73/7HIH9UGEnYkhjuIk4rQZmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnvGhp/btsFck49c73/7HIH9UGEnYkhjuIk4rQZmK/img.png&quot; data-alt=&quot;타임라인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnvGhp/btsFck49c73/7HIH9UGEnYkhjuIk4rQZmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnvGhp%2FbtsFck49c73%2F7HIH9UGEnYkhjuIk4rQZmK%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;1608&quot; height=&quot;629&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;타임라인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;팀마다 지라를 관리하는 방법이 다르지만 저희 팀 내에서 목표 설정과 일정 관리는 성공적이였으니 앞으로 &lt;i&gt;&lt;b&gt;새롭게 만난 팀에서 지라 관리 파악과 사용에서 조금 더 빠르게 적응&lt;/b&gt;&lt;/i&gt; 할 수 있을 거라는 자신감을 얻을 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자동배포&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;젠킨스&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 파이프라인에 다양한 빌드전, 빌드후 라이브러리를 제공&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. docker mount를 통해 쉬운 이식 기반의 확장성 제공&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 백엔드, 프론트엔드, db와 같은 도커 네트워크로 EC2 외부 포트 개방하지 않고 통신 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같은 이유로 젠킨스를 사용했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIiqOS/btsE6Vl2bjA/lzdLx3MpbohbBoX93cz6g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIiqOS/btsE6Vl2bjA/lzdLx3MpbohbBoX93cz6g1/img.png&quot; data-alt=&quot;시스템 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIiqOS/btsE6Vl2bjA/lzdLx3MpbohbBoX93cz6g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIiqOS%2FbtsE6Vl2bjA%2FlzdLx3MpbohbBoX93cz6g1%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;1269&quot; height=&quot;716&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시스템 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프론트, 백 모두 제가 배포를 맡아 진행했고 아래와 같은 순서로 진행됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;젠킨스 빌드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빌드한 파일 publish over SSH로 EC2로 전송&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EC2 내의 쉘 스크립트를 실행해 이전 이미지 제거 및 생성 후 실행&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;큰 도커 이미지(예: node나 nginx를 포함한 것)를 젠킨스에서 EC2로 전송하는 것을 최소화&lt;/b&gt;&lt;/i&gt;하기 위해 &lt;i&gt;&lt;b&gt;빌드 파일을 전송하고 배포하는 환경에서 이미지화&lt;/b&gt;&lt;/i&gt;를 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Application 설정파일, JASYPT 암호화&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lyL8n/btsE90mqILu/N2ghgOgc0GLKURiDDKlfik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lyL8n/btsE90mqILu/N2ghgOgc0GLKURiDDKlfik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lyL8n/btsE90mqILu/N2ghgOgc0GLKURiDDKlfik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlyL8n%2FbtsE90mqILu%2FN2ghgOgc0GLKURiDDKlfik%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;257&quot; height=&quot;193&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;백엔드는 application 설정파일은 로컬, 운영, db분리 등으로 나눠 jar 파일을 실행했습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E5Z54/btsE9QK15A8/yFe9yFZ26bOBg0arLpyQuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E5Z54/btsE9QK15A8/yFe9yFZ26bOBg0arLpyQuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E5Z54/btsE9QK15A8/yFe9yFZ26bOBg0arLpyQuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE5Z54%2FbtsE9QK15A8%2FyFe9yFZ26bOBg0arLpyQuk%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;1120&quot; height=&quot;657&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;657&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;1100&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD2xKt/btsFbsWWiQO/uPtOLpaGG6bxibfmpJlLyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD2xKt/btsFbsWWiQO/uPtOLpaGG6bxibfmpJlLyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD2xKt/btsFbsWWiQO/uPtOLpaGG6bxibfmpJlLyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD2xKt%2FbtsFbsWWiQO%2FuPtOLpaGG6bxibfmpJlLyK%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;1100&quot; height=&quot;297&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 설정파일은 jasypt를 통해 암호화 한 후 git에 올려 배포환경에서 환경주입 시 jasypt 암호만 주입할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트 코드&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHKMpt/btsFbiNE2Ha/jFbMTKlZxwbBcRNifrD3DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHKMpt/btsFbiNE2Ha/jFbMTKlZxwbBcRNifrD3DK/img.png&quot; data-alt=&quot;젠킨스 백엔드 아이템&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHKMpt/btsFbiNE2Ha/jFbMTKlZxwbBcRNifrD3DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHKMpt%2FbtsFbiNE2Ha%2FjFbMTKlZxwbBcRNifrD3DK%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;920&quot; height=&quot;604&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;젠킨스 백엔드 아이템&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백엔드는 테스트 코드를 지속적으로 작성해 새로운 서비스가 추가되거나 기존 서비스가 변경될 경우 테스트를 통해 문제를 확인해가며 자동배포를 진행했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CORS&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백엔드 개발도 했지만 인프라의 모든 부분을 혼자맡으면서 가장 머리털 빠지는 이슈였습니다. 배포환경의 백과 프론트 연결을 처음하는데 nginx를 활용해 백과 프론트의 주소를 외부로 노출하지 않고 리버스 프록시를 적용하면서 더욱 복잡했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 리버스 프록시 적용할 때 Origin 추가하기&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q7i8a/btsFbUlrMdk/DF7bLHmPF0VHncA6SWz5M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q7i8a/btsFbUlrMdk/DF7bLHmPF0VHncA6SWz5M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q7i8a/btsFbUlrMdk/DF7bLHmPF0VHncA6SWz5M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ7i8a%2FbtsFbUlrMdk%2FDF7bLHmPF0VHncA6SWz5M0%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;783&quot; height=&quot;140&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 스프링 시큐리티 Origin 허용 추가하기&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baXmKV/btsFbg98kLb/Ge0uY8lL07sxUO9WTvkyjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baXmKV/btsFbg98kLb/Ge0uY8lL07sxUO9WTvkyjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baXmKV/btsFbg98kLb/Ge0uY8lL07sxUO9WTvkyjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaXmKV%2FbtsFbg98kLb%2FGe0uY8lL07sxUO9WTvkyjk%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;1071&quot; height=&quot;346&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 시큐리티 설정 파일 내 CorsConfiguration 빈 등록시에 프론트의 Origin을 추가해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 스프링 시큐리티 PreFlight 허용 추가하기&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O72F1/btsE9JL2H29/pqN6qwpw5TNzgSJkZXqMr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O72F1/btsE9JL2H29/pqN6qwpw5TNzgSJkZXqMr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O72F1/btsE9JL2H29/pqN6qwpw5TNzgSJkZXqMr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO72F1%2FbtsE9JL2H29%2FpqN6qwpw5TNzgSJkZXqMr0%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;598&quot; height=&quot;88&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 문제를 다 해결해도 브라우저를 통해 요청을 할 때 GET 요청만 가능하고 나머지 요청은 다 막혔습니다.&amp;nbsp; OPTION http method를 활용해 해당 요청을 처리가능한지 확인하는 로직을 실행되어 해당 프로젝트 내의 모든 &lt;i&gt;&lt;b&gt;OPTION 요청을 허용해줘야 preFilght를 무사히 마치고 본 요청을 처리&lt;/b&gt;&lt;/i&gt;할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 프론트 Credentials 설정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쿠키를 사용한다면 프론트에서 추가로 해줘야할 설정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708519663043&quot; class=&quot;actionscript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. axios 전역 설정
axios.defaults.withCredentials = true; // withCredentials 전역 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;전반적인 백엔드 개발과 인프라 구축, 팀장의 역할을 맡으면서 진행한 팀 프로젝트는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;팀장으로서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생각보다 팀장역할이 힘들었지만 일정관리 부분에서 팀원의 역량을 파악하고 업무 분배하는법과 지속적으로 팀원들의 진행도를 파악하고 더딘 부분에 도움을 주고 받는 것의 중요성을 알 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백엔드 엔지니어로서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;통합 컨트롤러 테스트 or 포스트맨을 통한 테스트의 중요성과 환경마다 설정파일을 분리하는 법, 자신이 수정하는 부분에서 파생되어 팀원들의 로직에 변동이 있을 때 사전 협의 및 변화를 잘 알리는 것의 중요성을 배웠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인프라 엔지니어로서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배포환경의 백과 프론트 연결, TLS 인증서 발급을 통한 HTTPS 적용, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;S3 정적 웹 호스팅 HTTPS 적용&lt;/span&gt; , CORS 문제해결,&amp;nbsp; 보다 가벼운 배포 파일의 중요성과 개발자가 편한 자동배포까지는 진행해봤지만, 사용자가 편한 무중단 배포까지는 적용해보지 못해 해당 부분 공부중에 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rSI7Q/btsFaNUwviO/bva8jxK0Axorc3XVkbUaD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rSI7Q/btsFaNUwviO/bva8jxK0Axorc3XVkbUaD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rSI7Q/btsFaNUwviO/bva8jxK0Axorc3XVkbUaD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrSI7Q%2FbtsFaNUwviO%2Fbva8jxK0Axorc3XVkbUaD0%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;333&quot; height=&quot;56&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;약 4주간 정말 열심히 진행했고 천천히 백엔드 리팩토링과 인프라 변경을 거칠 예정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;읽어주셔서 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <category>CORS</category>
      <category>백엔드</category>
      <category>시작</category>
      <category>인프라</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/174</guid>
      <comments>https://anythingis.tistory.com/174#entry174comment</comments>
      <pubDate>Wed, 21 Feb 2024 22:05:28 +0900</pubDate>
    </item>
    <item>
      <title>[Toeic Speaking] IH 후기</title>
      <link>https://anythingis.tistory.com/173</link>
      <description>&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;1373&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4viGa/btsDKNgu6KU/PzNHCnKHc1JRPcM6m9NPY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4viGa/btsDKNgu6KU/PzNHCnKHc1JRPcM6m9NPY1/img.png&quot; data-alt=&quot;신나는 인증 ):&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4viGa/btsDKNgu6KU/PzNHCnKHc1JRPcM6m9NPY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4viGa%2FbtsDKNgu6KU%2FPzNHCnKHc1JRPcM6m9NPY1%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;1373&quot; height=&quot;247&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1373&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;신나는 인증 ):&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;취업준비를 하면서 어느정도 큰 회사는 지원을 위해 어학 성적을 요구해서 준비를 해야겠다고 생각했고 이전에 토익, 토스, 오픽을 준비해본적은 없었습니다. 알아보니 프리토킹이랑은 거리가 멀었기에 토익스피킹을 준비했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3주정도는 하루에 30분 정도 공부하고, 시험보기 1주일 전부터 하루 1시간&lt;/b&gt; 정도 공부했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;처음엔 유튜브 제이크쌤만 봤는데 모의고사 해설영상을 들어도 어느정도 템플릿이 있는 토익스피킹을 체계적으로 공부하고 있지 않은거 같아 &lt;b&gt;28시간에 끝내는 토익스피킹 스타트 책&lt;/b&gt;을 사고 유형별로 체계적으로 공부를 시작한 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;처음 공부하는데 &lt;b&gt;책에서 1주 or 2주 커리큘럼을 제시&lt;/b&gt;해줘서 공부하기 수월했고 유형별로 적용되는 템플릿의 틀을 익히고 나서부터는 유튜브에서 찾을 수 있는 토익 모의고사 + 모의고사 해설영상 보고 공부했습니다. 시험 3일 전부터는 시험장에서 &lt;b&gt;시끄러운 분위기에 적응하기 위해 공부하면서 카페 백색소음&lt;/b&gt;을 크게 틀고 공부했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;처음 시험보러 가기전에 제 목소리를 녹음해본적이 없어서 몰랐는데 마이크 테스트할 때 들어보니 &lt;b&gt;생각보다 더 크게 말해야&lt;/b&gt; 그나마 생기있게 들리는 느낌이였습니다. 다들 녹음하면서 공부하시면 좋을 것 같아요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;7번은 3가지 요인중 머리로 생각하던 것과 입으로 말하는 것이 다르게 나와 절고, 11번은 그냥 절어서 NH나올까봐 조마조마했는데 생각보다 후한점수를 받아 기분좋은 하루를 보낼 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;글 보시는 분들 모두 시험에 아는 문제 나오시고, 잘보셨으면 좋겠습니다 !&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>IH</category>
      <category>토익스피킹</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/173</guid>
      <comments>https://anythingis.tistory.com/173#entry173comment</comments>
      <pubDate>Fri, 19 Jan 2024 22:19:34 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 페치전략 + 영속성 컨텍스트 등록 방식에 따른 N+1 문제</title>
      <link>https://anythingis.tistory.com/172</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;페치 전략에 따라도 다르지만 영속성 컨텍스트에 어떻게 추가하는지에 따라 또 달라집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EAGER Fetch &lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EntityManager.find()&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;find 메소드와 EAGER 페치전략을 사용하면 inner join으로 N+1문제 없이 주로 같이 조회가 되는 연관관계의 경우에 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EntityManger.createQuery()&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EAGER 페치전략을 사용해도 EntityManger.createQuery(JPQL)를 사용하면 한 번의 쿼리에 정보를 모두 가져오지 않지만 EAGER 페치전략을 사용하기 때문에 곧바로 N+1 문제가 터집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Data JPA&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; JpaRepository의 기본구현체인 SimpleJpaRepository의 findById 와 findAll, 커스텀 find 메소드의 구현방법이 다릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;findById&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; EntityManager.find()를 사용해서 EAGER 전략과 함께 사용한다면 N+1 문제 없이 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; findAll, 파생 쿼리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부적으로&amp;nbsp; EntityManger.createQuery JPQL을 만들거나 EntityManger 내에서 JPQL 쿼리를 만들어 실행합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EAGER 페치 전략을 잘 사용하지 않겠지만 만약 사용할 때 위의 메소드들과 같이 사용하면 예상치않은 N+1 문제가 발생할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/JPA</category>
      <category>JPA</category>
      <category>N+1</category>
      <category>엔티티매니저</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/172</guid>
      <comments>https://anythingis.tistory.com/172#entry172comment</comments>
      <pubDate>Sun, 7 Jan 2024 15:47:42 +0900</pubDate>
    </item>
    <item>
      <title>[DB] 트랜잭션, 애플리케이션의 트랜잭션 제어</title>
      <link>https://anythingis.tistory.com/171</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션이란&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터를 저장할 때 단순히 파일에 저장해도 되지만, &lt;i&gt;&lt;b&gt;데이터베이스에 저장하는 가장 큰 이유&lt;/b&gt;&lt;/i&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션은 그대로 번역하면 거래인데, 데이터베이스에선 하나의 거래를 안전하게 처리하도록 보장합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;A가 B에게 5000원 이체한다면, 아래 두개의 작업이 하나처럼 이뤄져야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. A의 잔고 5000원 감소&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. B의 잔고 5000원 증가&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1번이 성공하고 2번이 실패하고 그대로 반영된다면 A의 잔고만 5000원이 감소하는 문제가 발생합니다. 데이터베이스는 트랜잭션을 적용해 거래 도중 하나라도 실패하면 거래 전의 상태로 되돌릴 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 작업이 성공해서 &lt;i&gt;&lt;b&gt;데이터베이스에 정상 반영하는 것을 Commit&lt;/b&gt;&lt;/i&gt;이라하고, &lt;i&gt;&lt;b&gt;작업 도중 하나라도 실패해 이전으로 되돌리는 것을 Rollback&lt;/b&gt;&lt;/i&gt;이라 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 ACID&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원자성&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 내에서 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공 하거나 모두 실패해야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일관성&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 합니다. 예를 들어 데이터베이스에서 정한 무결성 제약 조건을 항상 만족해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;격리성&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리합니다. 예를 들어 동시에 같은 데이터를 수정하지 못하도록 해야 합니다. 격리성은 동시성과 관련된 성능 이슈로 인해 &lt;i&gt;&lt;b&gt;트랜잭션 격리 수준(Isolation level)&lt;/b&gt;&lt;/i&gt;을 선택할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지속성&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 합니다. 중간에 시스템에 문제가 발생해도 데이터베이스 로그 등을 사용해서 성공한 트랜잭션 내용을 복구해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션은 원자성, 일관성, 지속성을 보장하지만 &lt;b&gt;&lt;i&gt;격리성을 완벽히 보장할수록 병렬처리가 불가능&lt;/i&gt;&lt;/b&gt;해져 동시 처리 성능이 매우 나빠집니다. 따라서 ANSI 표준은 트랜잭션 격리 수준을 4단계로 정의했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 격리수준&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;READ UNCOMMITED(커밋되지 않은 읽기)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다른 세션에서 변경하고 커밋되지 않은 내역도 접근가능해 데이터의 부정합 이슈가 큽니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;READ COMMITTED(커밋된 읽기)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;comit read.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tShgd/btsC7oI5pbT/lnpMOBu0hKUShuXK96rpC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tShgd/btsC7oI5pbT/lnpMOBu0hKUShuXK96rpC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tShgd/btsC7oI5pbT/lnpMOBu0hKUShuXK96rpC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtShgd%2FbtsC7oI5pbT%2FlnpMOBu0hKUShuXK96rpC0%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;762&quot; height=&quot;449&quot; data-filename=&quot;comit read.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커밋된 내역만 읽지만, 다른 세션의 트랜잭션 커밋 여부에 따라 같은 트랜잭션 내에서 같은 조회 시에 다른 결과를 얻을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;REPEATABLE READ(반복 가능한 읽기)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; READ COMMITTED와 비슷하지만 같은 트랜잭션 내에서 같은 조회를 할 때 다른 트랜잭션에서 수정중인 로우는 원본 테이블이 아닌 UNDO LOG 에 접근하기에 같은 결과를 얻습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SERIALIZABLE(직렬화 가능)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러 트랜잭션이 동일한 로우에 접근할 수 없으므로 데이터 부정합이 없지만 모든 트랜잭션이 순차적으로 처리되어 성능이 떨어집니다. 일반 SELECT 문도 배타락이 걸립니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터베이스 연결 구조와 DB 세션&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCL7Ff/btsC6qUVzcc/kwamV5X7frVeoaRiDJb0T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCL7Ff/btsC6qUVzcc/kwamV5X7frVeoaRiDJb0T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCL7Ff/btsC6qUVzcc/kwamV5X7frVeoaRiDJb0T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCL7Ff%2FbtsC6qUVzcc%2FkwamV5X7frVeoaRiDJb0T1%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;600&quot; height=&quot;191&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;272&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자가 WAS나 DB 접근 툴 같은 클라이언트를 사용해 데이터베이스 서버에 접근할 수 있습니다. 클라이언트 DB 서버에 연결을 요청하고 커넥션을 맺고, 이때 DB 서버는 내부에 세션을 만들어 해당 커넥션을 통한 모든 요청은 세션을 통해 실행됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 &lt;i&gt;&lt;b&gt;클라이언트(애플리케이션)을 통해 SQL을 전달하면 커넥션에 연결된 세션이 SQL을 실행&lt;/b&gt;&lt;/i&gt;합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;세션은 트랜잭션을 시작하고 커밋 또는 롤백을 통해 트랜잭션을 종료합니다. 이후 새로운 트랜잭션을 다시 시작할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자가 커넥션을 닫거나, DB관리자가 세션을 강제로 종료하면 세션은 종료됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc7DAl/btsC1pCGO8t/aAltOkd4a8LjQZYGJeLSx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc7DAl/btsC1pCGO8t/aAltOkd4a8LjQZYGJeLSx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc7DAl/btsC1pCGO8t/aAltOkd4a8LjQZYGJeLSx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc7DAl%2FbtsC1pCGO8t%2FaAltOkd4a8LjQZYGJeLSx1%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;600&quot; height=&quot;231&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션풀이 10개의 커넥션을 생성하면, 세션도 10개 만들어집니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션의 트랜잭션 제어&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;A가 B에게 5000원 계좌이체를 할 때는 아래의 작업이 한번에 이루어져야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;A의 잔고 5000원 감소&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;B의 잔고 5000원 증가&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;여러 SQL을 하나의 트랜잭션으로 관리하려면 같은 커넥션에서 실행&lt;/b&gt;&lt;/span&gt;되야하는데 그 이유는 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 서버에서 SQL 실행은 DB 세션이 담당합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동일한 DB 세션 내에서 여러개의 SQL을 한 개의 트랜잭션으로 관리합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 세션은 DB 커넥션을 통해 애플리케이션 DB 드라이버 커넥션과 연결됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동일한 DB 드라이버 커넥션을 통해 여러 SQL을 실행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션에서 커넥션풀에서 커넥션을 빌려 SQL을 실행할 때, DB는 Auto Commit이 Default로 켜져있습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; letter-spacing: 0px;&quot;&gt;따라서 애플리케이션에서 트랜잭션 제어는 아래의 과정을 거칩니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션 풀에서 커넥션 빌려오기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션의 Auto Commit 끄기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러개의 SQL을 동일한 커넥션으로 실행하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;- 1 중간에 예외처리 발생 시 롤백하기&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;- 2 모든 요청이 정상적으로 종료되면 커밋하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션의 Auto Commit 키기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션풀에 커넥션 반납하기&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;트랜잭션의 격리수준과 동시성 성능은 상반&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 세션과 커넥션의 연관관계&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션 단에서 여러 SQL의 한 트랜잭션 관리는 동일한 커넥션에서 실행&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704599595213&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;스프링 DB 1편 - 데이터 접근 핵심 원리 강의 - 인프런&quot; data-og-description=&quot;백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PNxlP/hyUXW3iDH7/utHtkYcxrQQtydrV4ORQGK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/16zoG/hyU2gy0yX7/xhL7yKsudwzMQMl1RjZHK1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bOXlb0/hyUXTMd6hI/RoSCFizyalI6hY3YIzaNo1/img.png?width=3000&amp;amp;height=2064&amp;amp;face=0_0_3000_2064&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PNxlP/hyUXW3iDH7/utHtkYcxrQQtydrV4ORQGK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/16zoG/hyU2gy0yX7/xhL7yKsudwzMQMl1RjZHK1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/bOXlb0/hyUXTMd6hI/RoSCFizyalI6hY3YIzaNo1/img.png?width=3000&amp;amp;height=2064&amp;amp;face=0_0_3000_2064');&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;스프링 DB 1편 - 데이터 접근 핵심 원리 강의 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/299&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mangkyu.tistory.com/299&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704599616712&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;[MySQL] 트랜잭션의 격리 수준(Isolation Level)에 대해 쉽고 완벽하게 이해하기&quot; data-og-description=&quot;이번에는 트랜잭션 격리 수준(Isolation Level)에 대해 알아보도록 하겠습니다. 아래의 내용은 RealMySQL과 MySQL 공식 문서 등을 참고하여 작성하였으며, 모든 내용은 InnoDB를 기준으로 설명합니다. 해당&quot; data-og-host=&quot;mangkyu.tistory.com&quot; data-og-source-url=&quot;https://mangkyu.tistory.com/299&quot; data-og-url=&quot;https://mangkyu.tistory.com/299&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GiqLb/hyUXROsu0r/YkrMeEoX8EhQVP9TAryksK/img.png?width=800&amp;amp;height=324&amp;amp;face=0_0_800_324,https://scrap.kakaocdn.net/dn/6rzTe/hyU2kakX3F/u5e8QDcP2Cgjf8eDtgZjV0/img.png?width=800&amp;amp;height=324&amp;amp;face=0_0_800_324,https://scrap.kakaocdn.net/dn/cmzjxZ/hyUXNZzPNq/VtoFbPZVk4oR5oXIRkiYbk/img.png?width=1524&amp;amp;height=1556&amp;amp;face=0_0_1524_1556&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/299&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangkyu.tistory.com/299&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GiqLb/hyUXROsu0r/YkrMeEoX8EhQVP9TAryksK/img.png?width=800&amp;amp;height=324&amp;amp;face=0_0_800_324,https://scrap.kakaocdn.net/dn/6rzTe/hyU2kakX3F/u5e8QDcP2Cgjf8eDtgZjV0/img.png?width=800&amp;amp;height=324&amp;amp;face=0_0_800_324,https://scrap.kakaocdn.net/dn/cmzjxZ/hyUXNZzPNq/VtoFbPZVk4oR5oXIRkiYbk/img.png?width=1524&amp;amp;height=1556&amp;amp;face=0_0_1524_1556');&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;[MySQL] 트랜잭션의 격리 수준(Isolation Level)에 대해 쉽고 완벽하게 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 트랜잭션 격리 수준(Isolation Level)에 대해 알아보도록 하겠습니다. 아래의 내용은 RealMySQL과 MySQL 공식 문서 등을 참고하여 작성하였으며, 모든 내용은 InnoDB를 기준으로 설명합니다. 해당&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangkyu.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;</description>
      <category>Computer Science/Database</category>
      <category>transaction</category>
      <category>트랜잭션</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/171</guid>
      <comments>https://anythingis.tistory.com/171#entry171comment</comments>
      <pubDate>Sun, 7 Jan 2024 12:55:32 +0900</pubDate>
    </item>
    <item>
      <title>[DB] 커넥션풀</title>
      <link>https://anythingis.tistory.com/170</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;노 커넥션 풀&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdhiP9/btsC7tcxFww/MYjoAKkPnBxDnbwKA6r1lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdhiP9/btsC7tcxFww/MYjoAKkPnBxDnbwKA6r1lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdhiP9/btsC7tcxFww/MYjoAKkPnBxDnbwKA6r1lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdhiP9%2FbtsC7tcxFww%2FMYjoAKkPnBxDnbwKA6r1lk%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;881&quot; height=&quot;344&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션에서 데이터베이스 커넥션 획득 과정&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 애플리케이션 로직은 DB 드라이버를 통해 커넥션 조회&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. DB 드라이버는 DB와 TCP/IP 커넥션을 연결 , 이 과정에서 3 way handshake 같은 TCP/IP 연결 을 위한 네트워크 동작이 발생&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. DB 드라이버는 TCP/IP 커넥션이 연결되면 ID, PW와 기타 부가정보를 DB에 전달&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. DB는 ID, PW를 통해 내부 인증을 완료하고, 내부에 DB 세션을 생성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5. DB는 커넥션 생성이 완료되었다는 응답&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;6. DB 드라이버는 커넥션 객체를 생성해서 클라이언트에 반환&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션도 &lt;i&gt;&lt;b&gt;TCP/IP 커넥션을 새로 생성하기 위해 리소스를 사용&lt;/b&gt;&lt;/i&gt;하고, DB도 &lt;i&gt;&lt;b&gt;커넥션과 세션&lt;/b&gt;&lt;/i&gt;을 생성해야합니다. 이는 SQL 실행하는 시간뿐만 아니라 커넥션을 새로 만드는 시간이 추가되어 결과적으로 &lt;i&gt;&lt;b&gt;응답속도에 영향&lt;/b&gt;&lt;/i&gt;을 줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런 문제를 해결하기 위해 커넥션을 미리 생성해두고 빌렸다가 반납하는 &lt;span style=&quot;color: #006dd7;&quot;&gt;커넥션 풀&lt;span style=&quot;color: #000000;&quot;&gt;입니&lt;/span&gt;&lt;/span&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션 풀&lt;/span&gt;&lt;/h3&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션 풀 초기화&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;861&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUGBf7/btsC7nJ7VFI/pNbKs0b2MSn69R4JtI0kyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUGBf7/btsC7nJ7VFI/pNbKs0b2MSn69R4JtI0kyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUGBf7/btsC7nJ7VFI/pNbKs0b2MSn69R4JtI0kyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUGBf7%2FbtsC7nJ7VFI%2FpNbKs0b2MSn69R4JtI0kyk%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;600&quot; height=&quot;283&quot; data-origin-width=&quot;861&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션 시작 시점에 필요한만큼 커넥션을 미리 확보해 풀에 보관합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;이때 커넥션을 연결하는 데에 오래 걸리기 때문에 커넥션을 만드는 과정은 애플리케이션 실행과 별도의 스레드에서 비동기로 진행해 애플리케이션 실행 속도를 늦추지 않습니다.&lt;/b&gt;&lt;/i&gt;&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션 풀에서 커넥션 빌려오기&lt;/span&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;868&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WLvIk/btsC4wA2Ofy/Iytuz7h65td9046D2caNiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WLvIk/btsC4wA2Ofy/Iytuz7h65td9046D2caNiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WLvIk/btsC4wA2Ofy/Iytuz7h65td9046D2caNiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWLvIk%2FbtsC4wA2Ofy%2FIytuz7h65td9046D2caNiK%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;600&quot; height=&quot;338&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션 로직 실행 후 커넥션 풀에 커넥션 반환&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxPfAb/btsC7oWBCtV/WUJlECcZPTlz7PmK8Bnrlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxPfAb/btsC7oWBCtV/WUJlECcZPTlz7PmK8Bnrlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxPfAb/btsC7oWBCtV/WUJlECcZPTlz7PmK8Bnrlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxPfAb%2FbtsC7oWBCtV%2FWUJlECcZPTlz7PmK8Bnrlk%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;600&quot; height=&quot;256&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션을 종료하는 것이 아닌 살아있는 상태로 커넥션 풀에 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보통의 커넥션 풀의 구현은&amp;nbsp; &lt;b&gt;커넥션을 닫는 로직이 실행&lt;/b&gt;되면 커넥션 풀에 반환됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때 커넥션의 상태가 다음 애플리케이션 로직 시에도 적용되기 때문에 autocommit 등 &lt;b&gt;상태를 초기화&lt;/b&gt;하고 닫습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 커넥션 생성은 코스트가 크기에 커넥션 풀로 관리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 커넥션 풀 초기화는 애플리케이션 실행하는 쓰레드와 다른 별도의 쓰레드에서 진행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 커넥션 풀에서 빌려온 커넥션을 닫으면 반환되고, 커넥션 상태를 초기화해야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;출처&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704526937215&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;스프링 DB 1편 - 데이터 접근 핵심 원리 강의 - 인프런&quot; data-og-description=&quot;백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/daKCRM/hyU2k2kMjt/qn6nAV7RZHYjkkWqEOsP6k/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/dtyHcz/hyU2sziU8c/B6e3QAdrUxmT48dm7vlABK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/6Dky1/hyUXXgEXLx/FJfRlueZZt6joPlPSz99bK/img.png?width=3000&amp;amp;height=2064&amp;amp;face=0_0_3000_2064&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/daKCRM/hyU2k2kMjt/qn6nAV7RZHYjkkWqEOsP6k/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/dtyHcz/hyU2sziU8c/B6e3QAdrUxmT48dm7vlABK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/6Dky1/hyUXXgEXLx/FJfRlueZZt6joPlPSz99bK/img.png?width=3000&amp;amp;height=2064&amp;amp;face=0_0_3000_2064');&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;스프링 DB 1편 - 데이터 접근 핵심 원리 강의 - 인프런&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.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;</description>
      <category>Computer Science/Database</category>
      <category>connection</category>
      <category>db</category>
      <category>커넥션</category>
      <category>커넥션풀</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/170</guid>
      <comments>https://anythingis.tistory.com/170#entry170comment</comments>
      <pubDate>Sat, 6 Jan 2024 16:42:59 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 자바 통신 인터페이스와 비동기 프로그래밍(feat: MVC, WebFlux)</title>
      <link>https://anythingis.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 백엔드개발자로 주로 &lt;span style=&quot;color: #409d00;&quot;&gt;클라이언트 요청을 받아 비즈니스 로직을 처리해 응답하는 부분&lt;/span&gt;을 처리하지만, 서버에서도 &lt;span style=&quot;color: #006dd7;&quot;&gt;외부 API를 사용하는 등 Http 요청&lt;/span&gt;을 통해 정보를 얻는 경우도 필요합니다.&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;통신.png&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UCHnY/btsCMw9G7X0/iK4AhGXxK4FEtgIUkNwDhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UCHnY/btsCMw9G7X0/iK4AhGXxK4FEtgIUkNwDhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UCHnY/btsCMw9G7X0/iK4AhGXxK4FEtgIUkNwDhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUCHnY%2FbtsCMw9G7X0%2FiK4AhGXxK4FEtgIUkNwDhk%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;600&quot; height=&quot;350&quot; data-filename=&quot;통신.png&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지금까지 진행한 프로젝트에서는 이 부분을 깊게 공부할 시간이 없어 모두 Spring이 제공하는 RestTemplate을 사용했지만 기반이 되는 부분을 조금 더 공부한 내용을 정리하기 위한 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RestTemplate&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring3.0부터 간편하게 &lt;b&gt;동기식&lt;/b&gt; Rest API 호출을 할 수 있게 해주는 클래스(Spring MVC 기반)입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JSON, XML, String 응답을 처리할 수 있고 Header와 Contetn-Type을 지정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Header, Content-Type 지정은 exchange()의 파라미터로 원하는 정보를 가진 HttpEntity를 사용해 추가 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RestTemplate docs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704000998274&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;RestTemplate (Spring Framework 6.1.2 API)&quot; data-og-description=&quot;postForLocation Create a new resource by POSTing the given object to the URI template, and returns the value of the Location header. This header typically indicates where the new resource is stored. URI Template variables are expanded using the given URI v&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&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;RestTemplate (Spring Framework 6.1.2 API)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;postForLocation Create a new resource by POSTing the given object to the URI template, and returns the value of the Location header. This header typically indicates where the new resource is stored. URI Template variables are expanded using the given URI v&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WebClient&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring5.0부터&amp;nbsp; &lt;i&gt;&lt;b&gt;Non-blocking&lt;/b&gt;&lt;/i&gt;한 방식을 지원하고 리액티브 타입(Mono, Flux)의 전송과 수신을 지원하는 인터페이스(Spring WebFlux 기반)입니다. Stream 결과의 전체를&amp;nbsp; 받는 Mono와 Stream을 개별적으로 컨트롤하는 Flux, 함수형 인터페이스들로 유연하고 리액티브하게 처리할 수 있습니다. Blocking 방식도 지원합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비동기 프로그래밍&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Client의 요청&lt;/b&gt;에 의해 RestTemplate을 사용하지만 &lt;b&gt;API 호출 결과과 Client의 응답으로 이어지지 않고&lt;/b&gt;, &lt;b&gt;처리시간이 오래&lt;/b&gt; 걸리는 로직은 &lt;b&gt;Client 요청과 분리&lt;/b&gt;해서 &lt;b&gt;&lt;i&gt;멀티쓰레드&lt;/i&gt;&lt;/b&gt;로 처리했습니다. WebClient에서는 어떻게 처리할 수 있을까 궁금했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RestTemplate(Spring MVC)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sync.png&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz0tHq/btsCQ5i7tts/sVf1cTuzJzrzeOVeTAUdh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz0tHq/btsCQ5i7tts/sVf1cTuzJzrzeOVeTAUdh0/img.png&quot; data-alt=&quot;동기, 일의 처리 순서가 보장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz0tHq/btsCQ5i7tts/sVf1cTuzJzrzeOVeTAUdh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz0tHq%2FbtsCQ5i7tts%2FsVf1cTuzJzrzeOVeTAUdh0%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;613&quot; height=&quot;176&quot; data-filename=&quot;sync.png&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동기, 일의 처리 순서가 보장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일의 &lt;b&gt;처리 순서를 보장&lt;/b&gt;하기 위해 일을 수행하는 동안 블로킹 됩니다. MVC 기반 RestTemplate 동작방식과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;멀티쓰레드를 사용한다면 동기 방식을 비동기적으로 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;multi.png&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cm9uWy/btsCN0bMnY4/a8pBY27EMqxUQtmxnoxi70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cm9uWy/btsCN0bMnY4/a8pBY27EMqxUQtmxnoxi70/img.png&quot; data-alt=&quot;스레드 사용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cm9uWy/btsCN0bMnY4/a8pBY27EMqxUQtmxnoxi70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcm9uWy%2FbtsCN0bMnY4%2Fa8pBY27EMqxUQtmxnoxi70%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;666&quot; height=&quot;232&quot; data-filename=&quot;multi.png&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스레드 사용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일의 처리 순서를 보장하는&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;동기 방식&lt;/span&gt;이지만, 스레드를 빌려와 일을 처리하면&amp;nbsp;&lt;b&gt;RestTemplate도 비동기적&lt;/b&gt;으로 사용할 수 있습니다.&amp;nbsp;하지만 스레드풀에서&amp;nbsp;&lt;span style=&quot;color: #1b711d;&quot;&gt;스레드를 빌려오는 비용&lt;/span&gt;,&amp;nbsp;&lt;b&gt;스레드 교체하는 비용&lt;/b&gt;, 레이스 컨디션을 고려해야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rest thread pool.png&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSHxzI/btsCRfzcJ8W/kVBKxFE2TcAcrVTcqRIYdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSHxzI/btsCRfzcJ8W/kVBKxFE2TcAcrVTcqRIYdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSHxzI/btsCRfzcJ8W/kVBKxFE2TcAcrVTcqRIYdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSHxzI%2FbtsCRfzcJ8W%2FkVBKxFE2TcAcrVTcqRIYdk%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;1183&quot; height=&quot;500&quot; data-filename=&quot;rest thread pool.png&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WebClient .subscribue of Webflux(Non-Blocking I/O)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;async.png&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGCmP/btsCQpvgnqb/Xbnw89X5q64tzbOf4QiAH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGCmP/btsCQpvgnqb/Xbnw89X5q64tzbOf4QiAH0/img.png&quot; data-alt=&quot;비동기, 동시에 여러 일을 수행할 수 있지만 처리 순서 보장 X&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGCmP/btsCQpvgnqb/Xbnw89X5q64tzbOf4QiAH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGCmP%2FbtsCQpvgnqb%2FXbnw89X5q64tzbOf4QiAH0%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;622&quot; height=&quot;272&quot; data-filename=&quot;async.png&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;비동기, 동시에 여러 일을 수행할 수 있지만 처리 순서 보장 X&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;같은 쓰레드에서 논블로킹으로 일을 동시에 처리하기 때문에 먼저 시작한 일이여도, 더 나중에 처리 될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;webclient.png&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yKG6M/btsCVJzgnbV/2Tpp6Xv9YK965jQcdhKvD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yKG6M/btsCVJzgnbV/2Tpp6Xv9YK965jQcdhKvD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yKG6M/btsCVJzgnbV/2Tpp6Xv9YK965jQcdhKvD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyKG6M%2FbtsCVJzgnbV%2F2Tpp6Xv9YK965jQcdhKvD1%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;999&quot; height=&quot;503&quot; data-filename=&quot;webclient.png&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현재 &lt;i&gt;백엔드에서 추세는 스레드를 적게 사용하면서 Non-Blocking I/O를 통해 전체적인 처리량을 늘리는 방향으로 발전중입니다. 요청을 Reactor Secheduler가 받아 실행중인 Thread의 Event Loop에 Job을 추가하거나, Thread를 빌려와 처리하는 Spring WebFlux의 추구 방향&lt;/i&gt;과 일치합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대용량 요청의 비동기 처리에서 스레드풀의 한계로 인해 MVC보다 Non-Block을 지원하는 WebFlux이 적은 스레드와 리소스를 사용해 효과적인걸 알게 됐습니다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;b&gt;가상 OS&lt;/b&gt;&lt;/span&gt; 보다는 &lt;b&gt;애플리케이션 추상화인 Docker&lt;/b&gt;, &lt;b&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Servlet들&lt;/span&gt;&lt;/b&gt; 보다는 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나의 Dispatcher Servlet과&amp;nbsp; Controller&lt;/span&gt;&lt;/b&gt; 등과 같이 한정적인 자원을 효율적으로 사용하기 위한 진화과정이라고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WebClient가 블로킹, 논블로킹을 모두 지원하지만 &lt;b&gt;Spring Webflux를 강제로 의존&lt;/b&gt;하게 됩니다. Servlet API 기반의 프로젝트였거나 동기적 처리가 필요해 모든 요청을 블로킹으로 처리할 경우 Spring 6.0부터는 WebClient와 사용방법이 유사한 &lt;b&gt;MVC 기반의 RestClient&lt;/b&gt;를 사용하시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=EJNBLD3X2yg&amp;amp;t=542s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=EJNBLD3X2yg&amp;amp;t=542s&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=EJNBLD3X2yg&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/b7PkVM/hyUTwcr1AZ/Z1wbncIFREY9Q0I5W6oZmK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/EJNBLD3X2yg&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/스프링</category>
      <category>mvc</category>
      <category>spring</category>
      <category>WebFlux</category>
      <category>비동기</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/169</guid>
      <comments>https://anythingis.tistory.com/169#entry169comment</comments>
      <pubDate>Sun, 31 Dec 2023 15:59:01 +0900</pubDate>
    </item>
    <item>
      <title>[Spring SSE] 서버에서 클라이언트로 실시간 알림 푸쉬 SSE</title>
      <link>https://anythingis.tistory.com/168</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구현하고 싶은 기능&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여행 관광지나 축제 리뷰를 지도로 관리하는 프로젝트를 진행하면서 사용자가 관심있는 지역을 팔로우하고, 그 지역에 새로운 관광지나 축제가 추가 될 때 알림을 보내고 싶었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일단 구현화면부터 보여드리겠습니다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지역 팔로우&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;시도_팔로잉.gif&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDmZpt/btsBq0C9yZz/5zJSKyOkyfqJz7j9AgoI4K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDmZpt/btsBq0C9yZz/5zJSKyOkyfqJz7j9AgoI4K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDmZpt/btsBq0C9yZz/5zJSKyOkyfqJz7j9AgoI4K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bDmZpt/btsBq0C9yZz/5zJSKyOkyfqJz7j9AgoI4K/img.gif&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;986&quot; height=&quot;972&quot; data-filename=&quot;시도_팔로잉.gif&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;972&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관리자가 새로운 컨텐츠 추가시 팔로워들에게 알림(좌측 화면 사용자/ 우측 화면 관리자)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;관리자_컨텐츠_추가_알람.gif&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/evKQ5v/btsBo7CVSKT/IjyhWwMdPdvmxKdayIJUe1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/evKQ5v/btsBo7CVSKT/IjyhWwMdPdvmxKdayIJUe1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/evKQ5v/btsBo7CVSKT/IjyhWwMdPdvmxKdayIJUe1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/evKQ5v/btsBo7CVSKT/IjyhWwMdPdvmxKdayIJUe1/img.gif&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;1870&quot; height=&quot;972&quot; data-filename=&quot;관리자_컨텐츠_추가_알람.gif&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SSE 선택 이유&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트에서 새로운 관광지나 축제가 생길시 서버에서 클라이언트로 단방향 알림 푸쉬가 필요했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요청을 주기적으로 보내면서 이벤트를 체크하는 polling은 http 오버헤드 때문에 서버의 부담이 크고, 양방향 통신이 필요없기에 websocket은 고려하지 않았습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;http 요청을 이벤트 발생시까지 유지하는 Long polling과 &lt;span style=&quot;background-color: #f8f8f8; color: #e76363; text-align: justify;&quot;&gt;text/event-stream &lt;/span&gt;타입의 http 연결을 유지하는 SSE 중 서버와 클라이언트 간의 연결이 유지되어 &lt;i&gt;&lt;b&gt;자주 이벤트가 발생할 때 클라이언트에 대한 부하가 상대적으로 적고&lt;/b&gt;&lt;/i&gt; Spring4.2부터 SSE Emitter를 지원해서 2주라는 빠듯한 프로젝트 일정에 빠르게 구현할 수 있을 것 같아 SSE를 선택했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;sse 구현&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;알림 발송 플로우&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 사용자가 관심 있는 지역 팔로우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 지역 관리자의 새로운 컨텐츠 추가&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 새로운 컨텐츠가 생기는 지역의 팔로워들에게 알림 발송&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3-1. 접속중인 사용자에게 SSE 사용해 실시간 알림 발송&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3-2. 서비스 미사용중인 사용자는 로그인 시에 SSE 연결 요청을 진행할 때 안읽은 알림 발송&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SSE Emmitters&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;public class SseEmitters {
// 접속자 관리 Thread-Safe한 자료구조 사용 필요
   private final Map&amp;lt;Long, SseEmitter&amp;gt; emitters = new ConcurrentHashMap&amp;lt;&amp;gt;();
   private static final Long DEFAULT_TIMEOUT = 1000L * 60;
   
 // 접속자 체크
   public boolean checkSse(Long accountId) { 
      if(emitters.get(accountId) == null) return false;
      return true;
   }
 
 // 사용자 로그인 시 SSE 연결 처리  
   public SseEmitter add(Long accountId, List&amp;lt;Notification&amp;gt; notifications) {
      SseEmitter emitter = new SseEmitter(DEFAULT_TIMEOUT);
      emitters.put(accountId, emitter);
      
       try {  
               emitter.send(SseEmitter.event()  
                       .name(&quot;connect&quot;)  
                       .data(&quot;connected!&quot;));  
           } catch (IOException e) {  
               throw new RuntimeException(e);  
           }  
      
      makeLoginNotification(accountId, notifications);
      
      emitter.onCompletion(() -&amp;gt; {
         log.info(&quot;onCompletion callback&quot;);
         this.emitters.remove(accountId); // 만료되면 리스트에서 삭제
      });

      emitter.onTimeout(() -&amp;gt; {
         log.info(&quot;onTimeout callback&quot;);
         emitter.complete();
      });

      return emitter;
   }

// 실시간 알림 푸쉬
   public void makeNotification(Long accountId, Long notificationId) {
      try {
         emitters.get(accountId)
         .send(SseEmitter.event()
               .name(&quot;notification&quot;)
               .data(String.valueOf(notificationId)));
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

// 로그인 시 안읽은 알림 푸쉬
   public void makeLoginNotification(Long accountId, List&amp;lt;Notification&amp;gt; notifications) {
      for (int i = 0; i &amp;lt; notifications.size(); i++) {
         try {
            makeNotification(accountId, notifications.get(i).getId());
         } catch (Exception e) {
            throw new RuntimeException(e);
         }
      }
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;emitter의 onCompletion과 onTimeout 콜백은 별도의 스레드에서 실행되기에 접속자 요청 컨트롤하는 SSE Emitter들을 관리하는 컬렉션은 &lt;b&gt;&lt;i&gt;Thread-Safe한 자료구조를 사용&lt;/i&gt;&lt;/b&gt;해야 concurrentModificationException이 발생하지 않습니다. &lt;i&gt;&lt;b&gt;SSE Emitter의 생성과 삭제, 연결 요청이 동시에 이뤄질 수 있어 여러 쓰레드에서 안전하게 사용할 수 있는 자료구조를 사용&lt;/b&gt;&lt;/i&gt;해야하기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;전체적인 코드 흐름을 볼 수 있는 프로젝트 레포지토리입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/JaegeonYu/yesterday-s-Trip&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701755774564&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - JaegeonYu/yesterday-s-Trip&quot; data-og-description=&quot;Contribute to JaegeonYu/yesterday-s-Trip development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; data-og-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/I0OU2/hyUE7c1LzM/hM2krWIJo5F7hhBvELkHHk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/JaegeonYu/yesterday-s-Trip&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/I0OU2/hyUE7c1LzM/hM2krWIJo5F7hhBvELkHHk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - JaegeonYu/yesterday-s-Trip&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to JaegeonYu/yesterday-s-Trip development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SSE 효율, 한계&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;Spring MVC 기반의 프로젝트에서 SSE 연결은 클라이언트와 서버를 지속적으로 연결하기에 스레드풀의 규모와 직접적인 연관이 있어 WebFlux 기반의 프로젝트보다 최대 연결 개수가 적습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;SSE 연결 관리를 메모리에서 관리하기에 서비스 규모가 작은 단일 서버에서는 효과적으로 실시간으로 단방향 통신을 할 수 있지만, 도메인 별로 서버를 나누는 MSA 환경이나 대규모 서비스에서 다중 서버 환경에서는 Redis Pub/Sub, Kafka를 덧붙이거나 Web Push API로 처리해야합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/spring-server-sent-events&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.baeldung.com/spring-server-sent-events&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #0070d1; text-align: start;&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1701756765942&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;Spring에서 Server-Sent-Events 구현하기&quot; data-og-description=&quot;&amp;hellip;&quot; data-og-host=&quot;tecoble.techcourse.co.kr&quot; data-og-source-url=&quot;https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/&quot; data-og-url=&quot;https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IthFa/hyUIB4Au6S/DtPEJJakT4meGeggRV2w81/img.png?width=423&amp;amp;height=276&amp;amp;face=0_0_423_276,https://scrap.kakaocdn.net/dn/cev133/hyUIwCelje/8fMhInAtPwhqJmdLkN4yD0/img.png?width=423&amp;amp;height=276&amp;amp;face=0_0_423_276,https://scrap.kakaocdn.net/dn/RatxQ/hyUFdYC1Qg/IcmW1FsAdohuzivrsOTfDk/img.png?width=2000&amp;amp;height=1330&amp;amp;face=0_0_2000_1330&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IthFa/hyUIB4Au6S/DtPEJJakT4meGeggRV2w81/img.png?width=423&amp;amp;height=276&amp;amp;face=0_0_423_276,https://scrap.kakaocdn.net/dn/cev133/hyUIwCelje/8fMhInAtPwhqJmdLkN4yD0/img.png?width=423&amp;amp;height=276&amp;amp;face=0_0_423_276,https://scrap.kakaocdn.net/dn/RatxQ/hyUFdYC1Qg/IcmW1FsAdohuzivrsOTfDk/img.png?width=2000&amp;amp;height=1330&amp;amp;face=0_0_2000_1330');&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;Spring에서 Server-Sent-Events 구현하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tecoble.techcourse.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sothoughtful.dev/posts/sse/&quot;&gt;https://sothoughtful.dev/posts/sse/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701756774729&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;SSE Spring에서 구현하기 A to Z&quot; data-og-description=&quot;SSE란?&quot; data-og-host=&quot;sothoughtful.dev&quot; data-og-source-url=&quot;https://sothoughtful.dev/posts/sse/&quot; data-og-url=&quot;https://mskim0425.github.io/posts/sse/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cKb60G/hyUIDOTqK2/wgpeAH5nMBrDC9kYSY4obK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://sothoughtful.dev/posts/sse/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sothoughtful.dev/posts/sse/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cKb60G/hyUIDOTqK2/wgpeAH5nMBrDC9kYSY4obK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&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;SSE Spring에서 구현하기 A to Z&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSE란?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sothoughtful.dev&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;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/702&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dkswnkk.tistory.com/702&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701756740691&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;SSE로 알림 기능 구현하기 with Spring&quot; data-og-description=&quot;서론 인터넷은 웹 브라우저와 웹 서버 간의 데이터 통신을 위해서 HTTP 표준 위에 구축되어 있습니다. 대부분의 경우 웹 브라우저인 클라이언트가 HTTP 요청을 서버에 보내고, 서버는 적절한 응답&quot; data-og-host=&quot;dkswnkk.tistory.com&quot; data-og-source-url=&quot;https://dkswnkk.tistory.com/702&quot; data-og-url=&quot;https://dkswnkk.tistory.com/702&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/louzB/hyUE4gjWO7/YaoiUMz85Bg4OwonX8mFkk/img.png?width=225&amp;amp;height=225&amp;amp;face=0_0_225_225,https://scrap.kakaocdn.net/dn/brsPvM/hyUE7jL0x4/WL8klVMyxFE0hyHNsfCjK1/img.png?width=225&amp;amp;height=225&amp;amp;face=0_0_225_225,https://scrap.kakaocdn.net/dn/b3NwqZ/hyUE1jz8BC/8PkfN0dACirZdautLtD621/img.jpg?width=1024&amp;amp;height=844&amp;amp;face=0_0_1024_844&quot;&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/702&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dkswnkk.tistory.com/702&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/louzB/hyUE4gjWO7/YaoiUMz85Bg4OwonX8mFkk/img.png?width=225&amp;amp;height=225&amp;amp;face=0_0_225_225,https://scrap.kakaocdn.net/dn/brsPvM/hyUE7jL0x4/WL8klVMyxFE0hyHNsfCjK1/img.png?width=225&amp;amp;height=225&amp;amp;face=0_0_225_225,https://scrap.kakaocdn.net/dn/b3NwqZ/hyUE1jz8BC/8PkfN0dACirZdautLtD621/img.jpg?width=1024&amp;amp;height=844&amp;amp;face=0_0_1024_844');&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;SSE로 알림 기능 구현하기 with Spring&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;서론 인터넷은 웹 브라우저와 웹 서버 간의 데이터 통신을 위해서 HTTP 표준 위에 구축되어 있습니다. 대부분의 경우 웹 브라우저인 클라이언트가 HTTP 요청을 서버에 보내고, 서버는 적절한 응답&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dkswnkk.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;</description>
      <category>개발로그</category>
      <category>spring sse</category>
      <category>sse</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/168</guid>
      <comments>https://anythingis.tistory.com/168#entry168comment</comments>
      <pubDate>Tue, 5 Dec 2023 15:14:28 +0900</pubDate>
    </item>
    <item>
      <title>[Mybatis] JPA 고수인 내가 Mybatis 세계에선 뉴비?</title>
      <link>https://anythingis.tistory.com/167</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사실 JPA 고수 아닙니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z2uv1/btsBjeh7rDm/a4jllpkw9HCocfzwKLxTD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z2uv1/btsBjeh7rDm/a4jllpkw9HCocfzwKLxTD1/img.jpg&quot; data-alt=&quot;죄송합니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z2uv1/btsBjeh7rDm/a4jllpkw9HCocfzwKLxTD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz2uv1%2FbtsBjeh7rDm%2Fa4jllpkw9HCocfzwKLxTD1%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;300&quot; height=&quot;168&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;죄송합니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 백엔드 시작을 김영한님의 강의를 보고 시작한만큼 DB접근 기술을 JPA밖에 몰랐었고, 올해 초에 프로그래머스에서 진행한 &lt;a href=&quot;https://anythingis.tistory.com/121&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;단순 CRUD 구현을 그만 !&lt;/a&gt; 스터디를 들으면서 JDBC Template를 써보긴했지만 스터디 커리큘럼을 따라가면서도 테이블 정의하는 것과 PrepareStatment로 쿼리짜는 게 귀찮아 &lt;i&gt;&lt;b&gt;DBA의 튜닝 쿼리를 빠르게 적용할 수 있겠다는 장점&lt;/b&gt;&lt;/i&gt;만 기억하는 것과 JPA를 쓰고 싶다는 생각만 했었던 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JPA를 한번 더 추상화한 스프링 데이터 JPA를 사용하면서 얼마나 많은 부분이 추상화된지 이론적으로 공부는 했지만 마이바티스를 다뤄보면서 직접 테이블 생성과 쿼리를 짜며 프로젝트를 진행하며 사용한 후기를 정리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Mybatis-Spring&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; background-color: #fafafa; color: #333333; text-align: start;&quot;&gt; 순수 마이바티스와 스프링과 연동되는 마이바티스 스프링은&amp;nbsp; 전체적으로 빌더로 관리하던 것을 빈으로 관리하면서 Thread - Safe해지는 것과 이름이 살짝식 바뀌는 차이가 있고 본 글은 마이바티스 스프링 기준으로 작성합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메인 내용은 아니라 접은글로 관리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;레거시 스프링 기준&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SessionFactoryBean&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Mybatis 설정파일을 바탕으로 SqlSessionFactory를 생성하고 빈 등록이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;dataSource 주입&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;typeAliasPackage, SQL 작성시 필요한 DTO alias 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;mapperLocations, SQL 작성한 구현체 XML의 위치 등록&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SqlSessionTemplate&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL 실행이나 트랜잭션 관리를 하는 핵심 클래스이고 SqlSession 인터페이스를 구현하며 Thread-Safe하고 빈 등록이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SessionFactoryBean 주입 필요&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MapperScannerConfigurer&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;mapper 인터페이스 스캔후 빈 등록해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트 기준&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1701668399157&quot; class=&quot;xml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.mybatis.spring.boot&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;mybatis-spring-boot-starter&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;2.3.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트 마이바티스 스타터 의존성 설정하고 Mapper 인터페이스에 @Mapper 어노테이션 붙이면 알아서 인식합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구현체를 별도의 xml로 관리하려면 application 설정파일에서 설정해줘야하지만 SQL 작성을 어노테이션으로 했기에 생략합니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 스키마 직접 관리&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1701669326049&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  jpa:
    hibernate:
      ddl-auto: create&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JPA를 사용할 땐 Application 설정파일에 이 한줄이면 만들어지던 테이블들을 직접 만들면서 구현하면서 수정이 필요한 부분 덕분에 컬럼을 추가, 삭제, 타입변경을 경험하며 DDL에 대해 익숙해졌습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. Insert 이후 같은 트랜잭션 내에서 Auto Increment 키 접근&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; 스프링 시큐리티/JWT로 인증, 인가를 관리해서 유저:권한(1:N)의 관계로 관리했습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkrCIs/btsBipEiuJb/gxk0MwEzYqrox9SMTPLyt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkrCIs/btsBipEiuJb/gxk0MwEzYqrox9SMTPLyt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkrCIs/btsBipEiuJb/gxk0MwEzYqrox9SMTPLyt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkrCIs%2FbtsBipEiuJb%2Fgxk0MwEzYqrox9SMTPLyt1%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;377&quot; height=&quot;225&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;회원가입시에 유저를 insert하고 해당 유저 id를 기준으로 권한 테이블에도 insert가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JPA&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701669558342&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
public class Account {
    @Id
    @GeneratedValue
    private Long id;
    
    ...생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; JPA는 Auto Increment 엔티티를 관리할 때 &lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;persist 시점에 바로 DB로 쿼리를 전송해 PK 값 생성 후 객체에 저장하기에 바로 키에 접근이 가능합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MyBatis&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701669524005&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Mapper
public interface AccountMapper {

    @Insert(&quot;insert into accounts(email, nickname, password, email_at) values (#{email} , #{nickname} , #{password}, now())&quot;)
    @Options(useGeneratedKeys = true, keyProperty = &quot;id&quot;)
    int save(Account account);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마이바티스는 @Options을 사용하면 Auto Increment 되는 키에 접근할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 쿼리압축 &amp;amp; 이유&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동일한 결과가 예상되지만 서버에서 많은 쿼리를 발생시키면 &lt;b&gt;네트워크 지연&lt;/b&gt;이나 &lt;b&gt;DB 시스템 쿼리 최적화 부담&lt;/b&gt;과 같은 성능 이슈가 생길 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쿼리 압축을 통해 하나의 쿼리가 무거워지면 DB에서 처리하는 시간이 오래걸리지만 압축하지 않은 것과 예상하는 결과는 어차피 같기 때문에 최대한 압축했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;N+1 문제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인증이 성공하면 이후 인가에서 사용하기 위해 JWT 토큰을 만들 때 유저의 간단한 정보와 권한 리스트를 함께 넣어줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701669069207&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@다양한 어노테이션 생략
public class Account {
    private Long id;
    private String email;
    private String nickname;
    private String password;
    private LocalDateTime emailAt;
    private List&amp;lt;Roles&amp;gt; roles;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때는 DB에서 유저 정보를 조회하고, 해당 유저의 권한들을 다시 조회하면 JPA에서 만났던 N+1문제가 발생합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JPA에서는 &lt;a href=&quot;https://anythingis.tistory.com/144&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Fetch Join&lt;/a&gt;이나 &lt;a href=&quot;https://anythingis.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EntityGraph&lt;/a&gt;로 해결할 수 있고,&amp;nbsp;MyBatis는 N+1을 1:1 관계에선 &lt;i&gt;Associate&lt;/i&gt;, 1:N 관계에선 &lt;i&gt;&lt;b&gt;Collections&lt;/b&gt;&lt;/i&gt;로 해결할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쿼리 압축&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701669212369&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Mapper
public interface AccountMapper {
    @Select(&quot;select id, email, password, nickname, email_at from accounts where email = #{email} &quot;)
    @Results({
    	@Result(property = &quot;id&quot;, column=&quot;id&quot;),
    	@Result(property = &quot;email&quot;, column=&quot;email&quot;),
    	@Result(property = &quot;nickname&quot;, column=&quot;nickname&quot;),
    	@Result(property = &quot;emailAt&quot;, column=&quot;email_at&quot;),
    	@Result(property = &quot;roles&quot;, javaType = List.class, column = &quot;id&quot;,
    	many = @Many(select = &quot;getRoles&quot;))
    })
    Account findByEmail(String email);
    
    // Collections 사용할 쿼리
    @Select(&quot;select role_id as roleId, account_id roleAccountId, role from roles where account_id = #{accountId}&quot;)
    List&amp;lt;Roles&amp;gt; getRoles(@Param(&quot;accountId&quot;) Long accountId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;유저가 권한을 들고 있는 상태로 조회하기 위해 select 문이 여러번 발생되고 아래와 같은 데이터를 매핑해서 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 86px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 35px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Account.id&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 35px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Account 정보..&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 35px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Role.id&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 35px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Role.Account_id&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 35px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Role&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;...&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ROLE_ADMIN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;...&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ROLE_USER&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;...&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ROLE_LEAD&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;중복되는 유저 정보에 해당하는 Role 정보들은 리스트로 Account.Roles에 담긴채 조회됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;INSERT INTO ~ SELECT&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/smqGi/btsBjpRkjhQ/CtLLxyRZ8Bgszro3KNxgrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/smqGi/btsBjpRkjhQ/CtLLxyRZ8Bgszro3KNxgrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/smqGi/btsBjpRkjhQ/CtLLxyRZ8Bgszro3KNxgrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsmqGi%2FbtsBjpRkjhQ%2FCtLLxyRZ8Bgszro3KNxgrk%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;620&quot; height=&quot;433&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 중에 유저가 특정 팀을 팔로우하고, 팀에 새로운 이벤트가 생기면 팀의 팔로워들에게 알림을 생성하는 로직이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;알림 생성 로직은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 팀의 팔로워들을 조회&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 해당 팔로워들에게 알림 생성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Insert는 항상 insert into values만 사용했어서&amp;nbsp;1번 쿼리로 조회한 팔로워들을 반복문을 통해 insert를 진행해서 수많은 쿼리를 발생시켰고,&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이건 줄여야겠다는 생각을 했습니다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SELECT의 다중행 결과를 컬럼에 맞게 조작해서 INSERT를 하면 다중행 삽입이 가능하고&amp;nbsp; &lt;i&gt;&lt;b&gt;팔로워 조회(1)+ 팀의 팔로워수(N)만큼&lt;/b&gt;&lt;/i&gt; 더 발생하던 쿼리를 &lt;i&gt;&lt;b&gt;하나&lt;/b&gt;&lt;/i&gt;로 압축할수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701671834368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Mapper
public interface TeamMapper {

	@Insert(&quot;INSERT INTO notification (account_id, sido_code, keyword, create_at, checked, content_type_id) &quot; + 
			&quot;SELECT f.account_id,  #{sidoCode}, #{keyword}, now(), false, #{contentTypeId} &quot;
			+ &quot;FROM follow f JOIN team t ON f.team_id = t.id &quot; + 
			&quot;WHERE t.id = #{teamId}&quot;)
	void insertNotification(@Param(&quot;teamId&quot;)Long teamId, @Param(&quot;sidoCode&quot;) Integer sidoCode, 
			@Param(&quot;keyword&quot;) String keyword, @Param(&quot;contentTypeId&quot;)Long contentTypeId);
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동적쿼리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마이바티스는 if, choosen, when, foreach 등을 사용할 수 있는 동적 쿼리를 지원합니다. 전 애플리케이션 단계에서 분기처리를 하고 막상 사용한건 foreach밖에 없지만 쓸모없는 쿼리를 줄이기 위해선 가장 많이 사용하는 것도 foreach 일것같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위에서 생성된 알림들 중 사용자가 체크박스에 체크하고 읽음처리하거나 전체 읽음처리를 할 때 사용한 쿼리입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702532170876&quot; class=&quot;less&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@Update(&quot;update notification set checked = true&quot;
			+ &quot;where id in&quot;
			+ &quot;&amp;lt;foreach collections='notifications' item='id' separator=',' open='(' close=')'&amp;gt;&quot;
			+ &quot;#{id}&quot;
			+ &quot;&amp;lt;/foreach&amp;gt;&quot;)
	void readNotifications(@Param(&quot;notifications&quot;)List&amp;lt;Notification&amp;gt; notifications);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 인라인 뷰(내부쿼리)에서 외부쿼리의 컬럼 접근&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kF3Za/btsBqr8dBmT/kG9piC8bIuEVQpInNoskF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kF3Za/btsBqr8dBmT/kG9piC8bIuEVQpInNoskF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kF3Za/btsBqr8dBmT/kG9piC8bIuEVQpInNoskF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkF3Za%2FbtsBqr8dBmT%2FkG9piC8bIuEVQpInNoskF1%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;396&quot; height=&quot;410&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 컨텐츠에 여러개의 리뷰가 달리고, 컨텐츠에서 리뷰 점수의 총합을 관리하고 컨텐츠의 리뷰 개수로 나눠 평균점수를 관리했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컨텐츠를 하나씩 조회할 때는 평균 점수를 구할 때 문제가 없었지만 평균 점수가 높은 컨텐츠 순서대로 limit를 걸어 사용자에게 추천하려고 할때 당연히 내부쿼리에서 외부 쿼리의 컬럼에 접근이 불가능한줄알고 일단 전체 컨텐츠의 평균점수를 가진 상태로 조회하고 애플리케이션 단계에서 사이즈로 관리를 했었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구글링을 통해 내부쿼리에서 외부쿼리의 컬럼에 접근이 가능하다는 것을 알고 평균 점수를 구하는 쿼리와 limit를 같은 쿼리내에서 처리했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701674765462&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Select(&quot;select content_id as contentId, content_type_id as contentTypeId, sido_code as sidoCode, gugun_code as gugunCode,&quot;
	+ &quot;title, address, tel, zipcode, image_url as imageUrl, latitude, longitude, mlevel, &quot;                         // 외부 쿼리 컬럼을 조인 조건으로 걸기
	+ &quot;total_score / (select count(*) from attraction_info a join review r on a.content_id = r.content_id where a.content_id = ai.content_id) as avgScore&quot;
	+ &quot; from attraction_info ai where ai.sido_code = #{sido} order by avgScore desc limit 3&quot;)
List&amp;lt;AttractionResDto&amp;gt; selectBySidoBestScoreLimit3(@Param(&quot;sido&quot;) Integer sido);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트를 마치고 테이블 생성의 제약조건부터 쿼리를 짜면서 원하는 기능을 구현하기 위한 DB설계 능력은 상승했다고 체감합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;왜냐면 테이블과 쿼리를 못짜면 원하는 기능구현이 안되기 때문에... 유저와 권한관리, 관리자와 사용자를 나눠보고, 일대일 대응관계는 하나의 기본키를 두개의 테이블에서 같이 써보고, 쿼리를 압축하기 위해 공부하면서 기초의 중요성을 깨달을 수 있었습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발로그</category>
      <category>JPA</category>
      <category>mybatis</category>
      <category>N+1</category>
      <category>spring</category>
      <category>동적쿼리</category>
      <category>스프링</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/167</guid>
      <comments>https://anythingis.tistory.com/167#entry167comment</comments>
      <pubDate>Mon, 4 Dec 2023 15:53:25 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 2023 장애인 분야 해커톤대회(장애플러스기술) 본선 입선 후기</title>
      <link>https://anythingis.tistory.com/166</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시연영상&lt;/span&gt;&lt;/h3&gt;
&lt;img style=&quot;max-width: 100%; height: auto;&quot; src=&quot;https://drive.google.com/uc?export=view&amp;id=1O7thO4-RfY3MU-3-wKiI29H2jHdsFfXM&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예선&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://kfpd.org/51/38&quot;&gt;https://kfpd.org/51/38&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701219678697&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;2023 장애인 분야 해커톤 대회 &amp;gt; [신청/결과] 사업신청 | 한국장애인재단&quot; data-og-description=&quot;우리나라 최초의 장애인을 위한 공익재단. 장애인의 사회 참여와 인권 증진을 위해 소중한 후원금을 투명하게 사용하고 성실하게 사업을 수행합니다.&quot; data-og-host=&quot;kfpd.org&quot; data-og-source-url=&quot;https://kfpd.org/51/38&quot; data-og-url=&quot;http://kfpd.org/51/38&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/CBATS/hyUE0QYFhh/3KkfDbURJs0tDDJfLSKZP1/img.jpg?width=700&amp;amp;height=455&amp;amp;face=0_0_700_455,https://scrap.kakaocdn.net/dn/UWav6/hyUFccPw5R/AcqCRDTaOYmLKUILYnG2Kk/img.jpg?width=700&amp;amp;height=455&amp;amp;face=0_0_700_455,https://scrap.kakaocdn.net/dn/bEKXWp/hyUCei5d56/FKpyYWyUXOpOsqBcySrjVk/img.jpg?width=901&amp;amp;height=1260&amp;amp;face=0_0_901_1260&quot;&gt;&lt;a href=&quot;https://kfpd.org/51/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kfpd.org/51/38&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/CBATS/hyUE0QYFhh/3KkfDbURJs0tDDJfLSKZP1/img.jpg?width=700&amp;amp;height=455&amp;amp;face=0_0_700_455,https://scrap.kakaocdn.net/dn/UWav6/hyUFccPw5R/AcqCRDTaOYmLKUILYnG2Kk/img.jpg?width=700&amp;amp;height=455&amp;amp;face=0_0_700_455,https://scrap.kakaocdn.net/dn/bEKXWp/hyUCei5d56/FKpyYWyUXOpOsqBcySrjVk/img.jpg?width=901&amp;amp;height=1260&amp;amp;face=0_0_901_1260');&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;2023 장애인 분야 해커톤 대회 &amp;gt; [신청/결과] 사업신청 | 한국장애인재단&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;kfpd.org&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장애인을 위한 앱 해커톤이 열려 대학 동기친구들과 좋은 기회인것 같아 참가했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfjTJQ/btsA7TRFEzi/WUHfbEFPH2hgM8d2FI33n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfjTJQ/btsA7TRFEzi/WUHfbEFPH2hgM8d2FI33n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfjTJQ/btsA7TRFEzi/WUHfbEFPH2hgM8d2FI33n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfjTJQ%2FbtsA7TRFEzi%2FWUHfbEFPH2hgM8d2FI33n1%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;1010&quot; height=&quot;401&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;401&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;팀원 세명 모두 취업준비중인 상황이였기에 아이디어톤보다는 실제 개발을 진행하는 메이커톤으로 참가했고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nm6g9/btsA6TRWYCK/GZEUzDqfmROZEJrLGzV0ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nm6g9/btsA6TRWYCK/GZEUzDqfmROZEJrLGzV0ok/img.png&quot; data-alt=&quot;덜컥 뽑혔습니다;;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nm6g9/btsA6TRWYCK/GZEUzDqfmROZEJrLGzV0ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnm6g9%2FbtsA6TRWYCK%2FGZEUzDqfmROZEJrLGzV0ok%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;1565&quot; height=&quot;807&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;덜컥 뽑혔습니다;;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AAC&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기획한 앱의 주요기능인 AAC에 대해 간단히 알려드리겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AAC는 보완대체의사소통수단으로 언어 및 의사소통의 장애를 가진분들을 위한말과 타이핑으로 정확한 의사소통이 어려울 때 의사성이 확실한 이모티콘이나 문장을 가르켜서 사용하는 것이 일반적입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WALTs/btsBkjBVV2c/GOMfnCnE520GxemfKKfoc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WALTs/btsBkjBVV2c/GOMfnCnE520GxemfKKfoc1/img.png&quot; data-alt=&quot;출처 - 나의 AAC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WALTs/btsBkjBVV2c/GOMfnCnE520GxemfKKfoc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWALTs%2FbtsBkjBVV2c%2FGOMfnCnE520GxemfKKfoc1%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;300&quot; height=&quot;535&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - 나의 AAC&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AAC 앱에 부족한 것&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 국내 AAC 앱은 언어 의사소통 장애와 손의 거동이 불편한 중복 장애인을 위한 아이트래킹 AAC앱의 부재&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 해외 AAC 앱을 사용하기 위해선 연동을 위한 하드웨어나, 아이트래킹이 탑재된 하드웨어가 필요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 해외 아이트래킹 하드웨어에 의존적이기에 비싸고(최소 50 ~ 기능에 따라 500만원) 복지적 지원이 어렵고, 현지화 필요&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://visual.camp/seeso-sdk/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://visual.camp/seeso-sdk/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701434151104&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;SeeSo, AI eye tracking SDK - VisualCamp&quot; data-og-description=&quot;SeeSo SDK will elevate your education, healthcare, uxui, ebook services. Find your user's attention rate and control app with your eye-gaze.&quot; data-og-host=&quot;visual.camp&quot; data-og-source-url=&quot;https://visual.camp/seeso-sdk/&quot; data-og-url=&quot;https://visual.camp/seeso-sdk/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/k6pg1/hyUE4zXqG3/0YZAV8p8RzTvC3XCaFGtC0/img.jpg?width=1024&amp;amp;height=311&amp;amp;face=0_0_1024_311,https://scrap.kakaocdn.net/dn/dpf1Iz/hyUE9BfdYR/EEYh3UFsJ0rGJNZvdlmlW1/img.jpg?width=1024&amp;amp;height=311&amp;amp;face=0_0_1024_311,https://scrap.kakaocdn.net/dn/sQqBl/hyUE1iUlpt/UXhQL3gQfDBjLFpW5SCZFk/img.jpg?width=2448&amp;amp;height=1836&amp;amp;face=539_589_898_982&quot;&gt;&lt;a href=&quot;https://visual.camp/seeso-sdk/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://visual.camp/seeso-sdk/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/k6pg1/hyUE4zXqG3/0YZAV8p8RzTvC3XCaFGtC0/img.jpg?width=1024&amp;amp;height=311&amp;amp;face=0_0_1024_311,https://scrap.kakaocdn.net/dn/dpf1Iz/hyUE9BfdYR/EEYh3UFsJ0rGJNZvdlmlW1/img.jpg?width=1024&amp;amp;height=311&amp;amp;face=0_0_1024_311,https://scrap.kakaocdn.net/dn/sQqBl/hyUE1iUlpt/UXhQL3gQfDBjLFpW5SCZFk/img.jpg?width=2448&amp;amp;height=1836&amp;amp;face=539_589_898_982');&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;SeeSo, AI eye tracking SDK - VisualCamp&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SeeSo SDK will elevate your education, healthcare, uxui, ebook services. Find your user's attention rate and control app with your eye-gaze.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;visual.camp&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같은 점들을 해결하기 위해 SW의 발전으로 기존의 스마트 기기의 전면카메라로 아이트래킹을 할 수 있는&amp;nbsp; Seeso SDK를 접목시키면 해외 아이트래킹 하드웨어에 의존적이지 않을 수 있고, 가격적으로도 기기마다 필요한 하드웨어와 달리 소프트웨어 사용료로 많이 낮출 수 있을 거라 예상하고 개발을 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;목표, 시스템 구성도, 화면구성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/LifeLikeMine/EyeCanTalk&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/LifeLikeMine/EyeCanTalk&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701219634773&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - LifeLikeMine/EyeCanTalk&quot; data-og-description=&quot;Contribute to LifeLikeMine/EyeCanTalk development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/LifeLikeMine/EyeCanTalk&quot; data-og-url=&quot;https://github.com/LifeLikeMine/EyeCanTalk&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bAuOoC/hyUCd5x7X9/lUVOIlUeqHdBu3S6FR3C0k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/LifeLikeMine/EyeCanTalk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/LifeLikeMine/EyeCanTalk&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bAuOoC/hyUCd5x7X9/lUVOIlUeqHdBu3S6FR3C0k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - LifeLikeMine/EyeCanTalk&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to LifeLikeMine/EyeCanTalk development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;레포지토리 정리해둔 것으로 대체하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백엔드를 하려고 들어갔는데 본선 OT때 장애인분들을 위해 접근성을 향상시킬 필요가 있고, 불필요한 회원 도메인은 지양하라고 하셔서 백엔드 서버가 아닌 전체적인 프로젝트 관리를 경험해볼 수 있었습니다. 기획과 필요한 아이트래킹 SDK를 찾아와 제공되는 데모앱 코드와 공식문서를 한줄한줄 살펴보고 앱개발하는 팀원과 함께 서비스 플로우를 구현해보고, 매주 진행하는 팀미팅에서 일정에 맞춰 팀원들과 소통하면서 팀과 함께 목표를 향해 달려가는 법을 배웠습니다. 또, 일반적인 개발 공모전이 아닌 장애인분들을 위한 개발 공모전을 하면서 더 많은 사람들에게 접근성이 높은 서비스를 만들어야겠다는 마음가짐도 배울 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd9iW3/btsA22vxniB/EbRiwoBxRHO6SBRPKw5pDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd9iW3/btsA22vxniB/EbRiwoBxRHO6SBRPKw5pDk/img.png&quot; data-alt=&quot;우황청심환과 함께한 발표사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd9iW3/btsA22vxniB/EbRiwoBxRHO6SBRPKw5pDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd9iW3%2FbtsA22vxniB%2FEbRiwoBxRHO6SBRPKw5pDk%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;600&quot; height=&quot;600&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;우황청심환과 함께한 발표사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아이디어 기획과 프로젝트 관리를 맡던대로 자연스럽게 최종발표도 진행했고, 입상은 못했지만&amp;nbsp; 200개가 넘는 팀들중에 본선 입선한 것과 팀 프로젝트와 많은 분들이 있는 자리에서 발표를 해볼수 있어 매우 좋은 경험이였습니다.&lt;/span&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;648&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvziBZ/btsA7MkIP2n/RuDz3sSx1pZRL7pBBvVuY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvziBZ/btsA7MkIP2n/RuDz3sSx1pZRL7pBBvVuY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvziBZ/btsA7MkIP2n/RuDz3sSx1pZRL7pBBvVuY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvziBZ%2FbtsA7MkIP2n%2FRuDz3sSx1pZRL7pBBvVuY0%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;400&quot; height=&quot;515&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 무엇보다 발표가 끝나고 집에 가기전, &lt;i&gt;&lt;b&gt;시민평가단중에 한분이 오늘 발표한 모든 앱 중에 자신이 일하시는 분야의 분들에게 필요한 인상 깊던 아이디어와 개발물이라고 해주신 것을 듣고 지금까지 취업을 위해서 개인프로젝트만 진행해와서 몰랐지만 개발자는 개발이 중점이 아닌 실제 문제를 해결하기 위해 프로그래밍을 이용하는 말을 이해할 수 있던시간이였습니다.&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;읽어주셔서 감사합니다~&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <category>eyecantalk</category>
      <category>Seeso SDK</category>
      <category>장애인 앱 해커톤</category>
      <category>장애플러스기술</category>
      <category>한국장애인재단</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/166</guid>
      <comments>https://anythingis.tistory.com/166#entry166comment</comments>
      <pubDate>Fri, 1 Dec 2023 21:42:38 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Spring without Spring Boot</title>
      <link>https://anythingis.tistory.com/165</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트로 스프링을 접해서 몰랐는데 레거시 스프링을 접하게 되면서 정말 스프링부트는 미쳤다는 걸 체감하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서블렛 컨테이너 등록 VS 내장 서블렛 컨테이너&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;레거시는 별도의 서블렛 컨테이너(웹 애플리케이션 서버)를 미리 준비하고 그 위에 스프링 프로젝트를 올린다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;이후 빌드할 때도 war파일을 서블렛 컨테이너에 올려야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1357&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SURma/btszSIcXR0y/r4exFZ0eWHdM38udy7oF81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SURma/btszSIcXR0y/r4exFZ0eWHdM38udy7oF81/img.png&quot; data-alt=&quot;톰캣 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SURma/btszSIcXR0y/r4exFZ0eWHdM38udy7oF81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSURma%2FbtszSIcXR0y%2Fr4exFZ0eWHdM38udy7oF81%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;1357&quot; height=&quot;261&quot; data-origin-width=&quot;1357&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;톰캣 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;839&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPM4M8/btszTyg1jBd/7JE168pDAnpIkF81SaD1WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPM4M8/btszTyg1jBd/7JE168pDAnpIkF81SaD1WK/img.png&quot; data-alt=&quot;톰캣으로 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPM4M8/btszTyg1jBd/7JE168pDAnpIkF81SaD1WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPM4M8%2FbtszTyg1jBd%2F7JE168pDAnpIkF81SaD1WK%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;839&quot; height=&quot;95&quot; data-origin-width=&quot;839&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;톰캣으로 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트는 기본적으로 서블렛 컨테이너가 내장되어 있어 별도의 서버 설치 없이 실행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빌드시에도 jar 파일만으로 배포할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qlmDs/btszUdKxEX6/RJxmJekl94irhPjKKhnMmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qlmDs/btszUdKxEX6/RJxmJekl94irhPjKKhnMmK/img.png&quot; data-alt=&quot;내장 톰캣으로 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qlmDs/btszUdKxEX6/RJxmJekl94irhPjKKhnMmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqlmDs%2FbtszUdKxEX6%2FRJxmJekl94irhPjKKhnMmK%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;836&quot; height=&quot;243&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내장 톰캣으로 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;web.xml&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ6VO7/btszLj6tsZY/BkikDjvmatRgF7ra1gqiWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ6VO7/btszLj6tsZY/BkikDjvmatRgF7ra1gqiWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ6VO7/btszLj6tsZY/BkikDjvmatRgF7ra1gqiWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ6VO7%2FbtszLj6tsZY%2FBkikDjvmatRgF7ra1gqiWK%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;1490&quot; height=&quot;666&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;spring legacy 프로젝트 생성시 자동으로 생성되긴 하지만 web.xml에 ContextLoaderListener를 통해 Dispatcher Servlet을 &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서블렛 컨테이너(톰캣)에 올리는 것을 명시한다. POST 요청 내에 한글 처리를 위한 UTF-8 필터도 수동으로 달아줘야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;root-context, servlet-context&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMKIA/btszQAM0mjG/oGlpQdVQ77BhZ7KyTadhK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMKIA/btszQAM0mjG/oGlpQdVQ77BhZ7KyTadhK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMKIA/btszQAM0mjG/oGlpQdVQ77BhZ7KyTadhK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMKIA%2FbtszQAM0mjG%2FoGlpQdVQ77BhZ7KyTadhK0%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;582&quot; height=&quot;536&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://anythingis.tistory.com/118#Root%20ApplicationContext%2C%20Servlet%20ApplicationContext-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WAS, Servlet Container&lt;/a&gt; 포스팅&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRmkat/btszQCRD1VL/EwSVz7apIOFk8rUEvXr8U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRmkat/btszQCRD1VL/EwSVz7apIOFk8rUEvXr8U0/img.png&quot; data-alt=&quot;servlet-context&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRmkat/btszQCRD1VL/EwSVz7apIOFk8rUEvXr8U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRmkat%2FbtszQCRD1VL%2FEwSVz7apIOFk8rUEvXr8U0%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;800&quot; height=&quot;356&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;servlet-context&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;servlet-context&lt;/b&gt;&lt;/i&gt;에 controller, viewResolver, resource 매핑같이 &lt;i&gt;&lt;b&gt;web과 관련된 빈을 등록&lt;/b&gt;&lt;/i&gt;하고, 컴포넌트 스캔을 xml로 진행한다. interceptor나 multipartResolver와 같이 웹에서 필요한 빈들도 수동으로 등록해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;root-context.xml&lt;/b&gt;&lt;/i&gt;은 &lt;i&gt;&lt;b&gt;web과 관련없는 빈&lt;/b&gt;&lt;/i&gt;들을 위한 컴포넌트 스캔, datasource 빈 등록을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트에서는 많은 설정 빈들이 내장되어 있고 application 설정파일과 어노테이션으로 대체된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Resource, View&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;webapp 밑의 다양한 설정파일은 추상화되어 사라지고, JSP가 있던 views는 템플릿엔진을 사용하는 resources.templates로 바뀐다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czYnG7/btszK3bEg3z/MF6KmaFUxjsE0tHX0Eoy6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czYnG7/btszK3bEg3z/MF6KmaFUxjsE0tHX0Eoy6K/img.png&quot; data-alt=&quot;spring legacy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czYnG7/btszK3bEg3z/MF6KmaFUxjsE0tHX0Eoy6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczYnG7%2FbtszK3bEg3z%2FMF6KmaFUxjsE0tHX0Eoy6K%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;340&quot; height=&quot;590&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;spring legacy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ptu9K/btszU3tRh9Q/1BVGPQZR6u4EYr0F5m5HjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ptu9K/btszU3tRh9Q/1BVGPQZR6u4EYr0F5m5HjK/img.png&quot; data-alt=&quot;spring boot&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ptu9K/btszU3tRh9Q/1BVGPQZR6u4EYr0F5m5HjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPtu9K%2FbtszU3tRh9Q%2F1BVGPQZR6u4EYr0F5m5HjK%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;330&quot; height=&quot;485&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;spring boot&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존성 관리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;레거시 스프링도 maven이나 gradle로 의존성 관리는 할 수 있지만 스프링과 연결되는 라이브러리의 버젼들을 따로 설정해야해 버젼 충돌이 잦을 수 있지만, 스프링부트는 스프링부트 버젼에 따라 연관된 라이브러리 버젼을 알아서 관리해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clXo3R/btszMa9cxsj/LIsClKQhpDlgTaSvQAjKL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clXo3R/btszMa9cxsj/LIsClKQhpDlgTaSvQAjKL1/img.png&quot; data-alt=&quot;각 라이브러리 버젼 컨트롤 필요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clXo3R/btszMa9cxsj/LIsClKQhpDlgTaSvQAjKL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclXo3R%2FbtszMa9cxsj%2FLIsClKQhpDlgTaSvQAjKL1%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;370&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;각 라이브러리 버젼 컨트롤 필요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Q1lh/btszTy89Xyn/foNSC4rWumNZFkiu20Bbs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Q1lh/btszTy89Xyn/foNSC4rWumNZFkiu20Bbs1/img.png&quot; data-alt=&quot;스프링 부트 버젼에 맞는 라이브러리 버젼 알아서 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Q1lh/btszTy89Xyn/foNSC4rWumNZFkiu20Bbs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Q1lh%2FbtszTy89Xyn%2FfoNSC4rWumNZFkiu20Bbs1%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;462&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스프링 부트 버젼에 맞는 라이브러리 버젼 알아서 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트는 GOAT다&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발/스프링</category>
      <category>java</category>
      <category>spring</category>
      <category>Spring Boot</category>
      <category>spring legacy</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/165</guid>
      <comments>https://anythingis.tistory.com/165#entry165comment</comments>
      <pubDate>Mon, 6 Nov 2023 20:16:22 +0900</pubDate>
    </item>
    <item>
      <title>[Spring]Spring AOP? AspectJ를 사용하는 이유</title>
      <link>https://anythingis.tistory.com/164</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AOP( &lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: left;&quot;&gt;Aspect-Oriented Programming&lt;/span&gt;)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체지향 프로그래밍은 클래스가 단일 책임원칙을 가지도록 설계해 응집도를 높이고 결합도는 낮춘다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 클래스의 비즈니스 로직을 제외한 로깅, 트랜잭션과 같은 부가기능 여러 클래스에 중복적으로 필요한 기능이 존재한다. 이런 경우 AOP를 통해 코드의 중복성을 낮추고, 부가기능의 응집도를 높혀 관리할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AOP 적용할만한 것들&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드 성능검사&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB와 엮인 대량의 데이터의 조회 쿼리 시간을 측정하는 등의 비즈니스 로직 호출의 앞뒤로 시간을 체크해 성능검사에 AOP를 적용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 처리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성능검사와 마찬가지로 비즈니스 로직의 전후에 설정되는 번거로운 try catch 대신 AOP를 적용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring AOP&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Target&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부가 관심사를 적용할 메인 관심 모듈(비즈니스 로직)&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Advice&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부가 관심 기능을 적용할 시점(메소드의 호출 전/후, 예외 발생 시)을 정의&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;태그&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Before&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드 실행 전 실행&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 15px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 15px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@AfterReturning&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 15px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드 정상 실행 후 실행&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@AfterThrowing&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드가 예외를 던질 시 실행(catch)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@After&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드가 정상, 예외 발생에 상관없이 실행(finally)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Around&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드의 실행 전, 후를 감싸 적용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JoinPoint&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부가 관심 기능을 적용할 지점 (method, field) , Target 객체가 구현한 모든 메소드&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;PointCut&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부가 관심 기능을 적용할 JoinPoint&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Advice를 적용할 Target의 메소드 선별할 정규표현식&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;b&gt;동적 Proxy를 기반으로 AOP를 구현하므로 메소드 JointPoint만 지원, &lt;a href=&quot;https://anythingis.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프록시란&lt;/a&gt;?&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AOP 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드의 성능검사를 위한 AOP 구현&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Dependencies&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1697785260435&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;

    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;5.3.27&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.aspectj&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;aspectjweaver&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;1.9.19&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;AspectJ의 이점&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring AOP 보다 정교한 포인트컷 정의와 공통 관심사 구현 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴파일 타임에 코드를 조작해 AOP 구현시 예외를 런타임이 아닌 컴파일 타임에 검출 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴파일 타임의 바이트 코드 조작은 런타임 성능 오버헤드를 감소 시킬 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Target&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1697784314915&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class StudentA {
	
	private String subject = &quot;수학&quot;;
	
	public void takeClass() {
		System.out.println(subject+&quot; 수업 듣는 중&quot;);
	}
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Aspect&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1697784857516&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Aspect
@Component // 스프링 컨테이너 등록 필요
public class AOP {
	 // 공통 관심사항 적용될 PointCut 정규표현식, test 패키지 내의 Student로 시작하는 클래스의
     // takeClass 메소드
	@Pointcut(&quot;execution(public * test.Student*.takeClass())&quot;)
	public void profileTtarget() {}
	
    // Adivce의 인자로 PointCut 필요, 성능검사를 위해 시작과 끝을 감싸는 @Around 선택
	@Around(&quot;profileTtarget()&quot;) 
	public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
		print(&quot;target : &quot; + joinPoint.getSignature().toShortString());
        // joinPoint의 클래스와 메소드 프린트
		
		long start = System.currentTimeMillis();
        // 타겟 메소드 실행
		Object result = joinPoint.proceed(); 
		long end = System.currentTimeMillis();
		
		print(&quot;performance time : &quot; + (end - start));
		return result;
		
	}

	private void print(String string) {
		System.out.println(&quot;======AOP======&quot;);
		System.out.println(string);
		System.out.println(&quot;===============&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Configuration&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1697785052851&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ComponentScan(&quot;test&quot;)
@Configuration
@EnableAspectJAutoProxy // Aspect Component 스캔시 필요한 어노테이션
public class AppConfig {

}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Main&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1697785077899&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {

	public static void main(String[] args) {
//		ApplicationContext context = new ClassPathXmlApplicationContext(&quot;test/applicationContext.xml&quot;);
		ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		
		StudentA a = context.getBean(StudentA.class);
		a.takeClass();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Console&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDq7wl/btsyR668vlx/SaG0F0uqI7tJGXSnI9eDKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDq7wl/btsyR668vlx/SaG0F0uqI7tJGXSnI9eDKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDq7wl/btsyR668vlx/SaG0F0uqI7tJGXSnI9eDKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDq7wl%2FbtsyR668vlx%2FSaG0F0uqI7tJGXSnI9eDKK%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;487&quot; height=&quot;242&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;알게 된 내용&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DispatcherServlet과 Handler 사이의 Interceptor와 예외를 모아 처리하는 ExceptionHandler는 관점을 분리하는 방법이지만 AOP는 특정 기능보다는 더 일반적인 적용범위를 가진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Transaction은 AOP가 맞다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>개발/스프링</category>
      <category>AOP</category>
      <category>aspectj</category>
      <category>spring</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/164</guid>
      <comments>https://anythingis.tistory.com/164#entry164comment</comments>
      <pubDate>Fri, 20 Oct 2023 16:11:18 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 의존성 주입의 종류</title>
      <link>https://anythingis.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring IoC Container가 Bean을 관리할 때는 객체관리 뿐아니라 객체 내에 필요한 객체를 의존성 주입으로 해결한다. 의존성 주입은 필요한 객체를 생성하는 것이 아닌 외부로 부터 객체를 받아 사용하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관련 작성 포스&lt;span style=&quot;background-color: #ffffff;&quot;&gt;팅 &lt;span style=&quot;background-color: #9d9d9d;&quot;&gt;&lt;a style=&quot;background-color: #9d9d9d;&quot; href=&quot;https://anythingis.tistory.com/133&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;의존성 주입은 왜 필요한가?&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링은 @Autowired 어노테이션을 이용한 다양한 의존성 주입 방법을 제공하는데&amp;nbsp;종류는 생성자, 수정자, 필드로 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;필드주입&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 코드가 간결하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 외부에서 변경이 불가능해 테스트시 모킹이 필요하다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 스프링 컨테이너 없이 의존성 주입이 힘들다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;객체가 생성된 이후에 주입하므로 final 키워드를 사용할 수 없어 해당 객체의 불변을 보장하지 못한다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681892406518&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ProductService {
    @Autowired
    private final ProductRepository productRepository;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;수정자(setter)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;객체가 생성된 이후에 주입하므로 final 키워드를 사용할 수 없어 해당 객체의 불변을 보장하지 못한다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;2. 불변성을 보장하지 못하는데 setter가 public으로 열려있어 객체가 바뀔 위험이 더 크다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681892552517&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ProductService {
    private ProductRepository productRepository;
	
    @Autowired
    public void setProductRepository(ProductReposiotry repo){
    	productRepository = repo;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생성자&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;1. 객체 생성시에 의존성을 주입해 final 키워드를 사용할 수 있어 불변성을 보장한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;2. 스프링 컨테이너에 의존하지 않고 순수 자바로 주입해줄 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;3. 2와 같은 이유로 테스트를 순수 자바로 할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681893147905&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ProductService {
    private final ProductRepository productRepository;
    
    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추천 방법(생성자)&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1697761685194&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@Component
public class A {
	
	@Autowired
	private B b;

	public void doAA() {
		b.doBB();
	}	
}

@Component
public class B {
	
	@Autowired
	private A a;

	public void doBB() {
		a.doAA();
	}
}

@Component
public class C {
	@Autowired
	private A a;
	@Autowired
	private B b;
	
	public void doCC() {
		a.doAA();
		b.doBB();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;필드와 세터주입은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;필드나 수정자 주입은 빈이 생성된 이후에 참조를 하기 때문에 순환참조가 있는&lt;/span&gt;&amp;nbsp;객체를 사용하기 전까지 예외발생을 일으키지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yu0wU/btsyQQibb0I/X89hby1hpMGGKz6frtKX01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yu0wU/btsyQQibb0I/X89hby1hpMGGKz6frtKX01/img.png&quot; data-alt=&quot;A와 B사이에 순환참조가 있지만 C의 doCC 메소드 실행 전까지는 예외를 발생할 수 없다가, doCC 메소드 호출시 A와 B가 서로를 호출해 StackOverflow가 발생&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yu0wU/btsyQQibb0I/X89hby1hpMGGKz6frtKX01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyu0wU%2FbtsyQQibb0I%2FX89hby1hpMGGKz6frtKX01%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;392&quot; height=&quot;78&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;A와 B사이에 순환참조가 있지만 C의 doCC 메소드 실행 전까지는 예외를 발생할 수 없다가, doCC 메소드 호출시 A와 B가 서로를 호출해 StackOverflow가 발생&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1697761960814&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class A {
	
	private B b;
	
	@Autowired
	public A(B b) {
		super();
		this.b = b;
	}

	public void doAA() {
		b.doBB();
	}
	
}

@Component
public class B {
	
	@Autowired
	public B(A a) {
		super();
		this.a = a;
	}

	private A a;

	public void doBB() {
		a.doAA();
	}
}&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;844&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cszINn/btsyLLpBNL3/s4GWX03rFQTlXQmMNVtGG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cszINn/btsyLLpBNL3/s4GWX03rFQTlXQmMNVtGG0/img.png&quot; data-alt=&quot;위와 같이 생성자로 주입시엔 런타임환경에서 스프링 컨테이너에 빈을 등록할 때 순환참조 예외를 발생&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cszINn/btsyLLpBNL3/s4GWX03rFQTlXQmMNVtGG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcszINn%2FbtsyLLpBNL3%2Fs4GWX03rFQTlXQmMNVtGG0%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;844&quot; height=&quot;62&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위와 같이 생성자로 주입시엔 런타임환경에서 스프링 컨테이너에 빈을 등록할 때 순환참조 예외를 발생&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;순환참조와 final 키워드를 통한 불변성 보장, 프레임워크에 의존하지 않는 순수자바로 주입이 가능한 이유들로 스프링도 생성자 주입을 권장한다&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;</description>
      <category>개발/스프링</category>
      <category>DI</category>
      <category>spring</category>
      <category>스프링</category>
      <category>의존성 주입</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/142</guid>
      <comments>https://anythingis.tistory.com/142#entry142comment</comments>
      <pubDate>Fri, 20 Oct 2023 09:38:19 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security 5.8 config requestMatchers</title>
      <link>https://anythingis.tistory.com/163</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오랜만에 스프링 시큐리티 설정을 건드리는데 원래 쓰던 &lt;s&gt;mvcMatchers&amp;nbsp;&lt;/s&gt;메소드가 가버렸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Security 5.8부터 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://docs.spring.io/spring-security/site/docs/5.8.6/api/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#antMatchers(java.lang.String%E2%80%A6%E2%80%8B)&quot;&gt;antMatchers&lt;/a&gt;&lt;span style=&quot;text-align: start;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://docs.spring.io/spring-security/site/docs/5.8.6/api/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#mvcMatchers(java.lang.String%E2%80%A6%E2%80%8B)&quot;&gt;mvcMatchers&lt;/a&gt;&lt;span style=&quot;text-align: start;&quot;&gt;, and&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://docs.spring.io/spring-security/site/docs/5.8.6/api/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#regexMatchers(java.lang.String%E2%80%A6%E2%80%8B)&quot;&gt;regexMatchers&lt;/a&gt; 가 deprecated되고 6부터는 사라질 예정이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 메소드 대신 새롭게 사용될 requestMatchers 메소드는 &lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://docs.spring.io/spring-security/reference/5.8/servlet/authorization/authorize-http-requests.html&quot;&gt;authorizeHttpRequests&lt;/a&gt;&lt;span style=&quot;text-align: start;&quot;&gt;,&amp;nbsp;&lt;/span&gt;authorizeRequests&lt;span style=&quot;text-align: start;&quot;&gt;, CSRF configuration,&amp;nbsp;&lt;/span&gt;WebSecurityCustomizer와 RequstMatcher 메서드가 있는 위치에 추가되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;requestMatchers는 애플리케이션의 클래스 경로에 Spring MVC가 있는 경우 mvcRequestMatcher 구현을 선택하고 없는 경우 AntPathRequestMatcher 구현을 선택해 더 안전한 기본값을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1694091730589&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    private final UserDetailsService userDetailsService;
    private final DataSource dataSource;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers(&quot;/&quot;, &quot;sign-up&quot;, &quot;check-email-token&quot;,
                        &quot;/email-login&quot;, &quot;/check-email-login&quot;, &quot;/login-link&quot;, &quot;/login-by-email&quot;,&quot;/search/team&quot;).permitAll()
                .mvcMatchers(HttpMethod.GET, &quot;/profile/*&quot;).permitAll()
                .anyRequest().authenticated();
         // ...
         return http.build();
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변경 코드&lt;/span&gt;&lt;/h3&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http,  HandlerMappingIntrospector introspector) throws Exception {
        MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath(&quot;/path&quot;);
        http
                .authorizeHttpRequests(authorize -&amp;gt; authorize
                        .requestMatchers(antMatcher(&quot;/&quot;), antMatcher(&quot;/sign-up&quot;)).permitAll()
                        .anyRequest().authenticated());


        return http.build();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;antMatcher 없이 쓰면 mvcMatcher가 잡혀야되는데 H2 console에 접근하는 웹주소인 JakartaWebServlet=[&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;/my-h2-console/&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;*]와 &lt;span&gt;&amp;nbsp;&lt;/span&gt;DispatcherServlet=[&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;/&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;]가 일치한다고 최근 스프링 시큐리티부터 servlet을 하나만 보호해주도록 바껴서 더 알아보고 적용하도록 해야겠다 ;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694095140008&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;Configuration Migrations :: Spring Security&quot; data-og-description=&quot;In 6.0, @Configuration is removed from @EnableWebSecurity, @EnableMethodSecurity, @EnableGlobalMethodSecurity, and @EnableGlobalAuthentication. To prepare for this, wherever you are using one of these annotations, you may need to add @Configuration. For ex&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html&quot; data-og-url=&quot;https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html&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;Configuration Migrations :: Spring Security&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In 6.0, @Configuration is removed from @EnableWebSecurity, @EnableMethodSecurity, @EnableGlobalMethodSecurity, and @EnableGlobalAuthentication. To prepare for this, wherever you are using one of these annotations, you may need to add @Configuration. For ex&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&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;</description>
      <category>개발/스프링</category>
      <category>antMatcher</category>
      <category>deprecated</category>
      <category>mvcMatcher</category>
      <category>spring security</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/163</guid>
      <comments>https://anythingis.tistory.com/163#entry163comment</comments>
      <pubDate>Thu, 7 Sep 2023 22:57:30 +0900</pubDate>
    </item>
    <item>
      <title>[SSAFY] 전공자 10기 합격 후기</title>
      <link>https://anythingis.tistory.com/162</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;SSAFY 면접 내용이나 코딩테스트 내용은 공유가 불가능합니다&lt;/b&gt;&lt;/span&gt;&lt;/h4&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;600&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbW6yM/btskSp6Mdcg/xY3w6hnkulLmRLReDr3lDK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbW6yM/btskSp6Mdcg/xY3w6hnkulLmRLReDr3lDK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbW6yM/btskSp6Mdcg/xY3w6hnkulLmRLReDr3lDK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbW6yM%2FbtskSp6Mdcg%2FxY3w6hnkulLmRLReDr3lDK%2Fimg.webp&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;600&quot; height=&quot;600&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스펙&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;국립 지방대 컴공 졸업(학점 3.96/4.5)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #9d9d9d;&quot;&gt;교내 코딩테스트 수상경험 1회&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;웹 개발 관련 프로젝트 2회&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정보처리기사 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;볼 품이 없습니다 .. ㅋㅋ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;에세이&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;500 ~ 600자가 그렇게 많지 않습니다. 경험과 동기를 적어야하는데 &lt;i&gt;&lt;b&gt;경험과 그에 따른 동기를 명확하게&lt;/b&gt; &lt;/i&gt;적으시면 될 것 같습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;첨삭 안받고 나름 잘썼다고 내고 면접 스터디를 하면서 후회 많이했습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; letter-spacing: 0px;&quot;&gt;오픈채팅방이나 주변사람들에게 부끄러워도 철판깔고 첨삭받는 걸 추천드립니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;코딩테스트&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백준 기준 실버를 쉽게 풀 수 있다면 어렵지 않을 겁니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;평소에 프로그래머스만 풀어보셨다면&amp;nbsp; 아래의 싸피 권장 문제 풀이 사이트에서 2 ~ 3 레벨 문제를 풀어보시면 도움이 될 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://swexpertacademy.com/main/main.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://swexpertacademy.com/main/main.do&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687393805929&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;SW Expert Academy&quot; data-og-description=&quot;SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!&quot; data-og-host=&quot;swexpertacademy.com&quot; data-og-source-url=&quot;https://swexpertacademy.com/main/main.do&quot; data-og-url=&quot;https://swexpertacademy.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Lev6E/hyS5CDJGgq/aNpWcVm8c2ChXMwf5NZ3M0/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/olL1P/hyS5vdypeu/Tdnw0YzaneZV6dS0KYrKrK/img.png?width=3063&amp;amp;height=2071&amp;amp;face=0_0_3063_2071,https://scrap.kakaocdn.net/dn/6wWhC/hyS5EVRIjs/oHDWb2z21wAdSnny16BOv1/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://swexpertacademy.com/main/main.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://swexpertacademy.com/main/main.do&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Lev6E/hyS5CDJGgq/aNpWcVm8c2ChXMwf5NZ3M0/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/olL1P/hyS5vdypeu/Tdnw0YzaneZV6dS0KYrKrK/img.png?width=3063&amp;amp;height=2071&amp;amp;face=0_0_3063_2071,https://scrap.kakaocdn.net/dn/6wWhC/hyS5EVRIjs/oHDWb2z21wAdSnny16BOv1/img.png?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080');&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;SW Expert Academy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;swexpertacademy.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;618&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ky8aa/btskRervKZ8/663VE4pEdcp0oNWK0C6wLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ky8aa/btskRervKZ8/663VE4pEdcp0oNWK0C6wLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ky8aa/btskRervKZ8/663VE4pEdcp0oNWK0C6wLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fky8aa%2FbtskRervKZ8%2F663VE4pEdcp0oNWK0C6wLK%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;618&quot; height=&quot;422&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;면접&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;면접 경험이 없거나 적다면 스터디를 하는 것을 추천드립니다.&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;면접 스터디를 통해 받은 피드백과 모의 면접을 통해 긴장감을 경험해본 과정이 도움이 많이 됐습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; letter-spacing: 0px;&quot;&gt;일주일정도 매일 진행했고, PT 면접에 대한 감을 잡은 후에 에세이 공유 및 피드백을 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;PT면접을 처음 접해보신다면 아래 영상을 보고 아무 IT 기사나 잡고 4~5번 정도 적용해보시면 감이 올겁니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=DOvCIrwMPbQ&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.youtube.com/watch?v=DOvCIrwMPbQ&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=DOvCIrwMPbQ&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/hXlx2/hyS4m3zRPe/meH252PYb02tDxbXxnyA81/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=472_184_676_406&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/DOvCIrwMPbQ&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;IT 기사도 자신의 생각을 정리하면서 읽으면 도움이 될 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://news.naver.com/main/main.naver?mode=LSD&amp;amp;mid=shm&amp;amp;sid1=105&quot;&gt;https://news.naver.com/main/main.naver?mode=LSD&amp;amp;mid=shm&amp;amp;sid1=105&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687394370904&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;IT/과학 : 네이버 뉴스&quot; data-og-description=&quot;모바일, 인터넷, SNS, 통신 등 IT/과학 분야 뉴스 제공&quot; data-og-host=&quot;news.naver.com&quot; data-og-source-url=&quot;https://news.naver.com/main/main.naver?mode=LSD&amp;amp;mid=shm&amp;amp;sid1=105&quot; data-og-url=&quot;https://news.naver.com/main/main.naver?mode=LSD&amp;amp;mid=shm&amp;amp;sid1=105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kno8E/hyS5xCrqdU/MyKKajGCtwzXJNJkeScdj0/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/cTE7ni/hyS4sJuLgO/SixyW8U6dqkXKGZPk6Ihm1/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420&quot;&gt;&lt;a href=&quot;https://news.naver.com/main/main.naver?mode=LSD&amp;amp;mid=shm&amp;amp;sid1=105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://news.naver.com/main/main.naver?mode=LSD&amp;amp;mid=shm&amp;amp;sid1=105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kno8E/hyS5xCrqdU/MyKKajGCtwzXJNJkeScdj0/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/cTE7ni/hyS4sJuLgO/SixyW8U6dqkXKGZPk6Ihm1/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420');&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;IT/과학 : 네이버 뉴스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;모바일, 인터넷, SNS, 통신 등 IT/과학 분야 뉴스 제공&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;최종발표&amp;nbsp;&lt;/span&gt;&lt;/h3&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;387&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DW6pY/btskRavO6My/VKpmZCypmQ2nO2Nv79i6KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DW6pY/btskRavO6My/VKpmZCypmQ2nO2Nv79i6KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DW6pY/btskRavO6My/VKpmZCypmQ2nO2Nv79i6KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDW6pY%2FbtskRavO6My%2FVKpmZCypmQ2nO2Nv79i6KK%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;387&quot; height=&quot;347&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;후기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스펙도 볼품없고&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;면접도 말싸움할 때 할 말은 생각 안나고 나중에 생각나서 그렇게 말할껄 하는것 같이 이불킥을 많이 갈겼습니다. 무엇보다 중요한 것은 에세이, 면접을 준비할 때 사람 구하는 것도 힘들고,&amp;nbsp; 모집하는 곳에 지원하는 것에도 부담을 느낄 수 있겠지만 사람들과 함께 준비하시는 걸 추천드립니다. 제3자가 나를 봤을 때 부족한 부분이 가장 잘보이고 이런 부분을 잘채워야 취업까지 도움이 되겠다는 걸 싸피 준비하면서 느꼈습니다. 앞으로 준비하시는 분들도 좋은 결과 있으시길 바랍니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>회고</category>
      <category>10기</category>
      <category>SSAFY</category>
      <category>전공</category>
      <category>합격</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/162</guid>
      <comments>https://anythingis.tistory.com/162#entry162comment</comments>
      <pubDate>Thu, 22 Jun 2023 10:01:55 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 양방향 연관관계와 단방향 연관관계</title>
      <link>https://anythingis.tistory.com/161</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQLMapper를 대충 경험하고 JPA를 시작해서 요즘 들어깨닫는게 많다. N+1 문제가 발생하는 원인과 해결하는 법을 알았는데 그 이전에 JPA를 SQLMapper처럼 사용할줄 알고(단방향 연관관계 사용)&amp;nbsp; 필요할 때 양방향 연관관계를 사용해야한다는 걸 알았고 정리하려고 글을 쓴다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JPA 단방향 연관관계&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;867&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIP3ID/btskg7T3l1V/f8EoKx3A4MTvRKdSK3AblK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIP3ID/btskg7T3l1V/f8EoKx3A4MTvRKdSK3AblK/img.png&quot; data-alt=&quot;오늘의 예시 글 - 댓글의 관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIP3ID/btskg7T3l1V/f8EoKx3A4MTvRKdSK3AblK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIP3ID%2Fbtskg7T3l1V%2Ff8EoKx3A4MTvRKdSK3AblK%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;712&quot; height=&quot;867&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;867&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오늘의 예시 글 - 댓글의 관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB설계에서 글과 댓글은 1:N의 관계를 갖는다. 이때 댓글은 글의 id를 외래키로 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼 예시와 같은 글과 댓글의 화면을 렌더링 해주려면 해당 글과 해당 글의 댓글을 가져와야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;글은 View 렌더링으로, 댓글은 Ajax로 해당 글의 id를 통해 비동기로 가져오는 서비스의 흐름을 가진다면&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;Entity&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = &quot;id&quot;)
public class Post extends {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(length = 50, nullable = false)
    private String title;
    @Column(columnDefinition = &quot;TEXT&quot;, nullable = false)
    private String content;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = &quot;id&quot;)
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String content;
    @Enumerated(EnumType.STRING)
    private Emotion emotion;

    @ManyToOne
    private Post post; // 외래키
    
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음과 같이 외래키로 갖는 쪽만 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;단방향 연관관계로&amp;nbsp;&lt;/span&gt;엔티티 설계를 하고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Repository&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public interface PostRepository extends JpaRepository&amp;lt;Post, Long&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public interface CommentRepository extends JpaRepository&amp;lt;Comment, Long&amp;gt; {
    List&amp;lt;Comment&amp;gt; findByPost(Post post);

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Flow&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;글에 대한 요청은 PostController -&amp;gt; PostService -&amp;gt; PostReposiotry를 통해 글만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;댓글에 대한 요청은 CommnetController -&amp;gt; CommentService -&amp;gt; CommentRepository를 통해 Post Id, EqualsHashCode를 통해 Post Id가 외래키로 걸린 댓글들을 조회해서 넘겨주면 된다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단방향 연관관계 특징&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;i&gt;&lt;b&gt;P&lt;/b&gt;&lt;b&gt;ost 도메인에서 Comment 도메인으로의 참조가 없고, Comment에서 Post로만의 참조만 있어 패키지 의존성을 줄일 수 있다&lt;/b&gt;&lt;/i&gt;.&amp;nbsp; 의존성을 줄일 수 있지만 Post -&amp;gt; Comment를 참조하고 싶다면 엔티티의 참조가 안되기 때문에 이런 방향의 참조가 필요하다면 양방향 연관관계를 사용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JPA 양방향 연관관계&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;867&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIP3ID/btskg7T3l1V/f8EoKx3A4MTvRKdSK3AblK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIP3ID/btskg7T3l1V/f8EoKx3A4MTvRKdSK3AblK/img.png&quot; data-alt=&quot;오늘의 예시 글 - 댓글의 관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIP3ID/btskg7T3l1V/f8EoKx3A4MTvRKdSK3AblK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIP3ID%2Fbtskg7T3l1V%2Ff8EoKx3A4MTvRKdSK3AblK%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;712&quot; height=&quot;867&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;867&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오늘의 예시 글 - 댓글의 관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 위 화면을 렌더링할 때 글과 댓글 모두 한번에 View 렌더링을 하고싶다면 ?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 글을 조회할 때 해당 글의 Id가 외래키로 걸린 댓글들을 관리하고 있으면 된다.&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;span&gt;Entity&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1687093070934&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = &quot;id&quot;)
public class Post extends {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(length = 50, nullable = false)
    private String title;
    
    @Column(columnDefinition = &quot;TEXT&quot;, nullable = false)
    private String content;
    
    @OneToMany(mappedBy = &quot;post&quot;) // 연관관계 주인이 아님을 뜻하는 mappedBy 사용
    private Set&amp;lt;Comment&amp;gt; comments = new HashSet&amp;lt;&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1687093124081&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = &quot;id&quot;)
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String content;
    @Enumerated(EnumType.STRING)
    private Emotion emotion;

    @ManyToOne
    private Post post; // 외래키, 양방향 연관관계에서 주인
    				   // 반대쪽에서 mappedBy를 사용하면 @JoinColumn을 생략할 수 있다.
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Repository&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public interface PostRepository extends JpaRepository&amp;lt;Post, Long&amp;gt; {
    @EntityGraph(attributePaths = &quot;comments&quot;)
    Optional&amp;lt;Post&amp;gt; findPostWithCommentsById(Long id); 
    // 글 조회후 댓글 조회할 때 발생하는 쿼리를 압축하기 위한 메소드
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Flow&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Controller
@RequiredArgsConstructor
public class PostController {
    private final PostService postService;

    @GetMapping(&quot;/posts/{postId}&quot;)
    public String get(@PathVariable Long postId, Model model) {
     
        Post post = postService.get(postId);
        if (post.getCommentResponses() != null) {
            model.addAttribute(&quot;comments&quot;, post.getComments());
        }
        model.addAttribute(&quot;post&quot;, post);
        return &quot;post/postView&quot;;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;글에 대한 요청이 들어올 때 &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;PostController -&amp;gt; PostService -&amp;gt; PostReposiotry를 통해 글을 가져오면서 댓글까지 한번에 가져와 넘겨줄 수 있다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; letter-spacing: 0px; color: #9d9d9d;&quot;&gt;엔티티 자체를 넘기고 참조 엔티티를 조회할 때 발생하는 순환참조는 DTO로 끊어줄 수 있지만 일단 엔티티를 사용한다.&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;span&gt;양방향 연관관계 특징&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Comment -&amp;gt; Post 참조뿐 아니라 Post 엔티티가 Comment들을 관리하면서 참조하기 때문에 패&lt;i&gt;&lt;b&gt;키지 의존성이 높아지며 순환참조가 발생할 수 있다&lt;/b&gt;&lt;/i&gt;.&amp;nbsp;&lt;/span&gt;&lt;span&gt; 양방향 연관관계를 맺을 때는 DB Write를 담당하는 외래키쪽과 읽기만 되는 mappedBy를 알아야 하며, DB에 영속성 컨텍스트가 플러쉬되기 전에 정보의 불일치가 있고, 같은 트랜잭션 내에서 이런 정보가 중요하다면 연관관계 편의메소드를 사용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정리(단방향을 알고 양방향을 쓰자)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예시는 예시일뿐 연관관계에 정답은 없고 서비스를 설계하고 구현해가면서 &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;왜 이 연관관계를 맺었는지에 대해 설명이 가능하면 될 것 같다. 양방향 연관관계를 먼저 알고 단방향으로 쓸 생각을 안하고 있었는데 백기선님 강의를 들으면서 단방향 연관관계를 어떻게 쓰는지에 대해 생각을 하다보니 객체참조보다는 DB의 외래키와 조인의 형태에 가깝다는 걸 느꼈다.&amp;nbsp; 김&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;영한님 강의를 들을 때 서비스 자체는 모두 단방향 연관관계로 끝내고, 필요하다면 양방향 연관관계를 맺으라는 말을 다시 한번 생각해볼 수 있었다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발/JPA</category>
      <category>JPA</category>
      <category>단방향 연관관계</category>
      <category>양방향 연관관계</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/161</guid>
      <comments>https://anythingis.tistory.com/161#entry161comment</comments>
      <pubDate>Sun, 18 Jun 2023 22:24:15 +0900</pubDate>
    </item>
    <item>
      <title>5월 회고</title>
      <link>https://anythingis.tistory.com/160</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;항상 흘러가면 흘러가는 대로 살아왔지만 취준생으로서 내가 한달동안 어떻게 살았는지 꼼꼼히 되돌아보고 앞으로 미래를 설계하기 위해서 달단위 회고를 해보려 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;잔디깎이범 퇴출&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;237&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bad5zo/btsikA4dEkB/UVeDvvfkr87twqtKDsTJek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bad5zo/btsikA4dEkB/UVeDvvfkr87twqtKDsTJek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bad5zo/btsikA4dEkB/UVeDvvfkr87twqtKDsTJek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbad5zo%2FbtsikA4dEkB%2FUVeDvvfkr87twqtKDsTJek%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;237&quot; height=&quot;148&quot; data-origin-width=&quot;237&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대학생 때는 학생 방패라도 있었지 지금은 아니다. 내 안의 잔디깎이범을 몰아냈다. 이 과정에서 쓸데없는 알고리즘 한문제씩을 올리는 기염을 토하기도 했지만 5월 말의 공백 한칸은 정말 공부한 것만 올리자는 마음가짐을 먹은 날이였다. 열심히 살자;;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;백기선님 웹 애플리케이션 강의 수강&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;나름 이전의 댓글 필터링 커뮤니티 프로젝트를 통해 스프링 MVC, 스프링 시큐리티, JPA를 헤딩하면서 나름 늘었다고 생각했었다. 하지만 구글링으로 막히면 해결하던 퀄리티와 달리 내가 고민했던 부분에 대한 베스트 프렉티스가 거의 이 강의에 담겨있다고 느꼈다.&lt;/span&gt;&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://anythingis.tistory.com/151&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Security 유저 정보 변경 시 Authentication.Principal 초기화&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;이 강의에서는 Authentication Principal에 한번 DB를 갔다와서 ID가 존재하는 Detached Entity를 이용해 세션에서의 유저정보를 관리하고 EntityManager에게 Merge를 통해 DB까지 유저정보를 저장한다. 하지만 나는 프로젝트 전체적으로 DirtyChecking을 통해 DB 유저정보를 관리하고 싶었다. Security Context로부터 받은 Detached Entity는 Repository에서 유저 검색을 위해 Id만 사용했고&amp;nbsp; DB 유저정보를 변경후에 Security Context의 세션 유저정보를 초기화시켜주는 방법을 채택했다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;Detached Entity와 Merge를 사용하면 별도의 Security Context는 관리가 필요없지만 merge 시에 값 전체가 대체되기 때문에 Security Context에 Entity 정보를 모두 저장해야한다는 리스크가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;내가 선택한 방법은 DirtyChecking으로 변경이 일어난 부분만 JPA가 알아서 바꿔줘서 데이터가 통으로 날라갈 일은 없지만 Security Context를 변경 시마다 초기화해줘야된다는 단점이 있다.&lt;/span&gt;&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://anythingis.tistory.com/145&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;뷰 반환 컨트롤러 레이어 테스트&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;MockMVC를 통해 뷰를 반환해도 뷰의 이름, 모델의 attirbute들 체크, 리다이렉션 시에 URL등을 체크할 수 있는 것을 알 수 있었다.&lt;/span&gt;&lt;span&gt;이전 댓글 필터링 프로젝트의 후기로 대문짝만하게 서버 사이드 렌더링 프로젝트라 컨트롤러 레이어 테스트를 하지 못했다는 망언을 바로 삭제할 수 있게된 계기였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://anythingis.tistory.com/148&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;OSIV를 통한 영속성 컨텍스트의 범위&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;컨트롤러 메소드에 트랜잭션이 안달렸지만 레포지토리나 서비스에서 읽어온 엔티티들을 지연로딩을 숨쉬듯 사용해온 입장으로서 가끔가다 기묘한 분위기를 느꼈는데 이는 OSIV(Open Session In VIew) 때문이였다. 기본값으로 켜져있으며 이 상태에서는 모두 영속성 컨텍스트의 범위이다. 트랜잭션 안에 있으면 수정도 가능하지만, 트랜잭션 밖의 컨트롤러 메소드에서도 영속성 엔티티를 통해 조회는 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;N+1 문제&lt;span style=&quot;color: #006dd7;&quot;&gt;(&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://anythingis.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EntityGraph&lt;/a&gt;)&amp;nbsp; , (&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://anythingis.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;QueryDsl&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;JDBC, MyBatis 등 얕은 Sql Mapper 경험만 하고 JPA를 다룬 대가중에 가장 크다고 할 수 있겠다. Sql Mapper를 통해 DB에서 데이터를 가져오면 발생하고 싶어도 발생할 수 없는 문제를 너무 추상화된 JPA를 바로 사용해서 이 문제점을 잘 모르고 더티체킹, 양방향 연관관계, 연관관계의 주인, 연관관계 편의메서드 이런쪽만 공부한 것 같다. 일단 JPA를 사용하고 쿼리를 사용할 때 가장 중요한 것은 서비스에서 필요한만큼의 정보를 가져와야한 다는 것이다. 너무 적어도, 너무 많아도 안된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bauvde/btsijndymqU/7VxD7118AgFDLKmwIVs8CK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bauvde/btsijndymqU/7VxD7118AgFDLKmwIVs8CK/img.png&quot; data-alt=&quot;프로젝트 전체적인 패키지 의존도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bauvde/btsijndymqU/7VxD7118AgFDLKmwIVs8CK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbauvde%2FbtsijndymqU%2F7VxD7118AgFDLKmwIVs8CK%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;428&quot; height=&quot;287&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로젝트 전체적인 패키지 의존도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Entity
@Getter
@EqualsAndHashCode(of = &quot;id&quot;)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Team {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany
    @Builder.Default
    private Set&amp;lt;Account&amp;gt; managers = new HashSet&amp;lt;&amp;gt;();

    @ManyToMany
    @Builder.Default
    private Set&amp;lt;Account&amp;gt; members = new HashSet&amp;lt;&amp;gt;();

    @Column(unique = true)
    private String path;

 
    @ManyToMany
    @Builder.Default
    private Set&amp;lt;Tag&amp;gt; tags = new HashSet&amp;lt;&amp;gt;();

    @ManyToMany
    @Builder.Default
    private Set&amp;lt;Zone&amp;gt; zones = new HashSet&amp;lt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;Team 엔티티의 대략적인 연관관계는 다음과 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;Team은 매니저가 멤버, 지역정보, 관심태그를 관리한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;만약 지역정보를 추가하기 위해 Team 엔티티를 조회해온다면 접근하는 유저가 매니저인지 확인이 필요할 것이고, 팀의 지역정보도 가져와서 추가해야할 것이다. 이런 경우의 서비스 로직을 위해서 레포지토리, 엔티티 단에서는 매니저, 지역정보가 포함된 Team 정보를 넘겨줘야 한다.&amp;nbsp; 만약 그냥 findById로 팀을 찾아온다면 매니저를 체크할 때 매니저를 조회하는 쿼리가 발생하고, 지역정보를 추가하기 위해 지역정보를 조회하는 쿼리가 발생할 것이다. 모든 연관관계 맵핑을 지연로딩으로 처리하는 것이 좋은 이유는 &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;참조 엔티티에 접근할 수 있어서 좋은 것이 아니라 &lt;/span&gt;바로 내가 가져오고 싶은 정보 외에 것은 가져오지 않기위해 좋은 것이다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://anythingis.tistory.com/158&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 이벤트, 비동기 멀티 쓰레드&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;패키지 의존성을 낮추고 멀티 쓰레드를 통해 느린 이메일 서비스를 사용자의 경험에서 삭제하는 과정을 배웠다. Kafka보다 매우 순한 맛이고 충분히 내가 프로젝트에 이용할정도로 유익한 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Builder 잘쓰자&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Builder가 파라미터 가독성을 좋게하고, 파라미터 타입이 같은 때에 안헷갈리게 해주는 장점을 가지지만 @AllArgsContruct와 함께 써버리면 이후 파라미터에 들어가지 않은 값은 기본값도 안들어간다. @Builder.Default나 필요한 생성자를 만들고 @Builder를 쓰자&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;6월 예고&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트 만들기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배운것은 바로 써먹어야한다. 강의를 들으며 중간중간 내 입맛으로 바꾼 부분도 있고 미리 구현하고 강의 후에 수정했지만 조금 더 다듬기 위해 프로젝트를 하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공모전&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;6월 9일 장애인 앱 공모전 예선 결과가 나온다. 예선 통과하면 앱을 구현해야해서 만약 붙게되면 배운것을 여기다 써먹을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;알고리즘&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;알고리즘을 하루에 최소 두문제는 풀고있는데 과연 늘기는하는걸까 하는 생각이 든다. 그래도 알고리즘을 잘하면 비즈니스 로직을 짤때 많은 도움이 되겠구나 하는걸 이번달 강의 들으면서 느꼈다. 열심히 늘려서 내가 원하는 서비스를 만드는 그날이 오길;;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EPoAj/btsil0ahzQs/G3ta0XeO7btVMveQ1qYkCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EPoAj/btsil0ahzQs/G3ta0XeO7btVMveQ1qYkCk/img.png&quot; data-alt=&quot;잔디깎이범과의 사투말고도 꾸준히 나와 싸울 6월이겠지만 열심히 살아보자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EPoAj/btsil0ahzQs/G3ta0XeO7btVMveQ1qYkCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEPoAj%2Fbtsil0ahzQs%2FG3ta0XeO7btVMveQ1qYkCk%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;300&quot; height=&quot;429&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잔디깎이범과의 사투말고도 꾸준히 나와 싸울 6월이겠지만 열심히 살아보자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;</description>
      <category>회고</category>
      <category>백기선</category>
      <category>회고</category>
      <author>비오베베</author>
      <guid isPermaLink="true">https://anythingis.tistory.com/160</guid>
      <comments>https://anythingis.tistory.com/160#entry160comment</comments>
      <pubDate>Thu, 1 Jun 2023 23:03:18 +0900</pubDate>
    </item>
  </channel>
</rss>