<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sua_ahn.log</title>
        <link>https://velog.io/</link>
        <description>해보자구</description>
        <lastBuildDate>Tue, 01 Oct 2024 11:09:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sua_ahn.log</title>
            <url>https://velog.velcdn.com/images/sua_ahn/profile/424b1270-3832-4cc5-9727-2f90b1437712/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sua_ahn.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sua_ahn" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[오라클 기본 구조와 원리 (2)]]></title>
            <link>https://velog.io/@sua_ahn/%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-2</link>
            <guid>https://velog.io/@sua_ahn/%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-2</guid>
            <pubDate>Tue, 01 Oct 2024 11:09:40 GMT</pubDate>
            <description><![CDATA[<p>SQLP 노트 정리본 📚</p>
<h1 id="6-문장-수준-읽기-일관성">6. 문장 수준 읽기 일관성</h1>
<p>: 단일 SQL 수행 중 다른 트랜잭션이 데이터를 변경해도 일관성 있는 결과 리턴</p>
<p>1) 커밋되지 않은 값 읽기 허용 X
= Dirty Read 방지</p>
<p>2) 커밋 시점에 따라 다른 결과 리턴 X</p>
<h2 id="consistent-모드-블록-읽기">Consistent 모드 블록 읽기</h2>
<p>of Multi-Version Read Consistency Model</p>
<h3 id="📌-블록">📌 블록</h3>
<p><strong>1) Current Block</strong> : 원본 → Only one</p>
<p><strong>2) CR Block</strong> : 복사본 → 여러 버전 OK</p>
<h3 id="📌-scn-system-commit-number">📌 SCN (System Commit Number)</h3>
<p>: 시점 정보를 담은 전역변수</p>
<p><strong>1) 블록 SCN</strong> 
: 블록의 마지막 변경 시점 in Block Header
(변경시킨 트랜잭션의 커밋 SCN)</p>
<p>*<em>2) 커밋 SCN *</em>
: 트랜잭션 별 커밋 번호 in ITL Slot</p>
<p>*<em>3) 쿼리 SCN *</em>
: 쿼리가 실행되는 시점의 DB 상태 (스냅샷 SCN)</p>
<h3 id="📌-규칙">📌 규칙</h3>
<p>블록 SCN이 쿼리 SCN보다 작거나 같은 블록만 읽을 수 있다.
(SCN 확인과정 필요)</p>
<p><strong>A. Current Block SCN ≤ 쿼리 SCN</strong>
= 쿼리가 시작된 이후로 변경 없음
→ Current Block Read 즉, CR 생성하지 않고 그대로 읽음
<em>*Current 모드 읽기 아님 주의!</em></p>
<p><strong>B. Current Block SCN &gt; 쿼리 SCN</strong>
= 쿼리가 시작된 이후로 변경 있음
→ CR Cloning 후, 쿼리 SCN보다 낮은 마지막 Committed 시점으로 Undo (ITL슬롯의 UBA 사용)</p>
<p><strong>C. Current Block Status = Active</strong>
= 레코드에 Lock Byte 설정은 돼있는데, ITL슬롯에 아직 커밋 정보 기록 안됨
= 커밋 시 바로 클린아웃하지 않기 때문에, 갱신중이라고 단정할 수 없음
→ 블록 클린아웃 시도 ⇒ A or B</p>
<p>&nbsp;</p>
<hr>
<h1 id="7-consistent-vs-current-모드-읽기">7. Consistent VS Current 모드 읽기</h1>
<p>1) Consistent 모드로 갱신할 경우 (가정)
→ Lost Update 발생</p>
<p>2) Current 모드로 갱신할 경우 (다른 DBMS의 경우)
→ 갱신/삭제 중 다른 트랜잭션에 의해 갱신/삭제 대상이 변할 수 있음</p>
<p>3) Consistent 모드로 읽고, Current 모드로 갱신할 경우 (가정)
→ 잘못된 이중 갱신 발생</p>
<p>4) Consistent 모드로 갱신대상 식별하고, Current 모드로 갱신할 경우⭐
→ 갱신대상의 rowid 추출하고, 이 rowid와 조건절에 모두 해당되는 대상을 건건이 갱신</p>
<p>⇒ 갱신중에 갱신대상 범위에 새로 들어오지 못함
But, 갱신중에 갱신대상에서  <strong>제외</strong>될 수 있음</p>
<p>&nbsp;</p>
<blockquote>
<p>스칼라 서브쿼리는 일반적으로 Consistent 모드로 읽음
<code>UPDATE A SET COL = A.a + (SELECT b FROM B)</code>
But, 갱신해야할 테이블의 컬럼을 참조하는 경우 Current 모드로 읽음
<code>UPDATE A SET COL = (SELECT A.a + B.b FROM B)</code></p>
</blockquote>
<p>&nbsp;</p>
<hr>
<h1 id="8-블록-클린아웃">8. 블록 클린아웃</h1>
<p>: <strong>로우 Lock</strong> 해제 + <strong>블록헤더에 커밋 정보</strong> 기록
 &nbsp; Lock Byte -------------&gt; ITL Entry</p>
<h3 id="1-delayed-block-cleanout">1) Delayed Block Cleanout</h3>
<p> : 트랜잭션이 갱신한 블록 개수가 총 버퍼캐시 블록 개수의 1/10을 초과할 때, 커밋 이후 해당 블록을 <strong>액세스</strong>하는 첫 쿼리에 의해 완전히 정리</p>
<ul>
<li>ITL Slot에 커밋 정보 저장</li>
<li>Lock Byte 해제</li>
<li>Online Redo logging</li>
</ul>
<h3 id="2-커밋-클린아웃--fast-block-cleanout">2) 커밋 클린아웃 = Fast Block Cleanout</h3>
<p> : 트랜잭션이 갱신한 블록 개수가 총 버퍼캐시 블록 개수의 1/10을 초과하지 않을 때, 커밋시점에 곧바로 <strong>불완전한 클린아웃</strong>을 수행
 (ITL Slot에 커밋 정보만 저장)</p>
<p>→ 해당 블록을 <strong>갱신</strong>하는 다음 트랜잭션에 의해 완전히 클린아웃
즉, Current 모드로 읽는 시점에야 Lock Byte를 해제하고 Delayed Redo logging</p>
<p>&nbsp;</p>
<hr>
<p>참고 도서 : 오라클 성능 고도화 원리와 해법 1</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오라클 기본 구조와 원리 (1)]]></title>
            <link>https://velog.io/@sua_ahn/%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-1</link>
            <guid>https://velog.io/@sua_ahn/%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-1</guid>
            <pubDate>Tue, 01 Oct 2024 09:42:48 GMT</pubDate>
            <description><![CDATA[<p>SQLP 노트 정리본 📚</p>
<h1 id="1-기본-아키텍쳐">1. 기본 아키텍쳐</h1>
<h3 id="1-데이터베이스">1) 데이터베이스</h3>
<p>: 데이터를 저장하는 파일 집합 = 디스크에 저장된 데이터 집합</p>
<h3 id="2-sga-공유-메모리--캐시-영역">2) SGA 공유 메모리 = 캐시 영역</h3>
<p>  (1) DB 버퍼 캐시    ━━━━━━━━━━━┐</p>
<p>  (2) Redo 로그 버퍼 ━━━━━━ 데이터 입출력을 빠르게 하기 위함</p>
<p>  (3) Shared Pool
      &nbsp;&nbsp;① 데이터 딕셔너리 캐시 ━━━━━━━┛
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 로우 캐시 (∵로우 단위 I/O)</p>
<pre><code>  &amp;nbsp;&amp;nbsp;② 라이브러리 캐시 </code></pre><h3 id="3-프로세스">3) 프로세스</h3>
<p>  (1) 서버 프로세스     → DBMS</p>
<p>  (2) 백그라운드 프로세스 : DBWn, LGWR, SMON, PMON, RECO 등</p>
<blockquote>
<p><strong>⭐ 인스턴스 = SGA 공유 메모리 + 프로세스</strong></p>
</blockquote>
<p>※ 오라클 접속 과정
<img src="https://velog.velcdn.com/images/sua_ahn/post/00127958-a1df-4118-bf2a-4697162f356c/image.jpg" alt="오라클 접속 과정"></p>
<p>&nbsp;</p>
<hr>
<h1 id="2-db-버퍼-캐시">2. DB 버퍼 캐시</h1>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/a5d64395-ae96-42da-986d-d60655bd2194/image.jpg" alt="버퍼캐시"></p>
<blockquote>
<p>⭐ <strong>래치 Latch</strong>
: SGA에 공유된 자료구조를 보호하기 위해
엑세스를 직렬화하는 Lock 메커니즘</p>
</blockquote>
<ul>
<li>캐시버퍼체인 래치 : 여러 해시체인 관리</li>
<li>캐시버퍼 LRU체인 래치 : LRU리스트 보호</li>
</ul>
<p>&nbsp;</p>
<hr>
<h1 id="3-버퍼lock">3. 버퍼Lock</h1>
<h3 id="버퍼블록-읽기">버퍼블록 읽기</h3>
<p>DBA → 해시버킷 → <strong>해시체인래치</strong> 획득 </p>
<p>→ 버퍼블록을 찾았으나, 다른 프로세스가 버퍼Lock </p>
<p>→ 버퍼헤더에 있는 버퍼Lock <strong>대기자목록</strong>에 등록 </p>
<p>→ 래치 해제 → buffer busy waits 이벤트 발생 </p>
<p>⌈버퍼⌉ → 캐시된 버퍼핸들이 다 사용중이면 cache buffer handles래치 획득 
|Lock| → <strong>버퍼핸들</strong> 획득 
⌊획득⌋ → 버퍼헤더에 있는 소유자목록에 버퍼핸들 연결 즉, <strong>pin 설정</strong></p>
<h3 id="버퍼lock의-필요성">버퍼Lock의 필요성</h3>
<p>: 오라클은 블록단위 I/O를 수행하므로, 블록 자체로의 진입을 직력화해야 정합성 유지 가능</p>
<h3 id="버퍼-pinning">버퍼 Pinning</h3>
<p>: 하나의 DB call동안 버퍼Pin 유지
→ 래치 획득 과정 생략하여 논리적 블록 읽기 횟수 ⇓</p>
<p>ex1) 인덱스 스캔 중, 인덱스와 테이블 교차 방문 시
ex2) 인덱스 스킵스캔 중, 인덱스 브랜치 블록 pinning</p>
<p>&nbsp;</p>
<hr>
<h1 id="4-redo">4. Redo</h1>
<h3 id="redo-목적">Redo 목적</h3>
<p><strong>1) DB 복구</strong>
: 디스크가 깨지면 Achived Redo 로그 사용</p>
<p><strong>2) 캐시 복구</strong>
: 정전으로 인스턴스 종료되면 Online Redo 로그로 트랜잭션 재현 (인스턴스 복구 시 Roll forward 단계)
→ 이후 롤백해서 트랜잭션 복구</p>
<p><strong>3) Fast Commit</strong>
: 커밋 시 Redo 로그를 append하고, 데이터파일에는 나중에 기록</p>
<blockquote>
<p>데이터 블록에 커밋정보를 기록하고, 
로우 Lock 해제를 지연시킴 (Delayed Cleanout)</p>
</blockquote>
<p>→ 오라클은 Lock매니저없이 레코드 속성으로 Lock을 구현하므로, 일일이 레코드를 찾아 Lock을 해제하는 것은 비효율적</p>
<p>&nbsp;</p>
<h3 id="lgwr가-redo-로그를-기록하는-시점">LGWR가 Redo 로그를 기록하는 시점</h3>
<p>1) 커밋 or 롤백 명령 시 (<strong>Log Force at Commit</strong>)
: 트랜잭션의 영속성 보장</p>
<p>2) 3초마다 DBWR로부터 신호를 받을 때 (<strong>Write ahead logging</strong>)
: Dirty 버퍼를 파일에 기록하기 전 로그 먼저 기록</p>
<p>(Instance crash가 발생하여 roll forward 후 rollback을 하는 과정에서, 데이터파일에는 있으나 Redo 로그에는 없으면 롤백이 아닌 커밋이 됨)</p>
<p>3) 로그버퍼의 1/3이 차거나, 1MB가 넘을 때
: <strong>대량 트랜잭션 대비</strong></p>
<p>&nbsp;</p>
<hr>
<h1 id="5-undo">5. Undo</h1>
<h3 id="undo-목적">Undo 목적</h3>
<p>1) 트랜잭션 롤백
2) 트랜잭션 복구 (인스턴스 복구 시 rollback 단계)
3) <strong>읽기 일관성 Read Consistency</strong> ⭐</p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/461af8b7-51f1-43c4-a883-6218f5544dcb/image.jpg" alt="undo segment"></p>
<h3 id="transaction-table-slot">Transaction Table Slot</h3>
<p>: 트랜잭션 시작을 위해 확보해야 함</p>
<p>① 트랜잭션 ID &nbsp; &nbsp; &nbsp;② 커밋 SCN
③ 트랜잭션 Status ④ Last UBA</p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/e2881d41-3c48-445b-a302-26082d8c48a5/image.jpg" alt="data block"></p>
<h3 id="interested-transaction-list-slot-itl-slot">Interested Transaction List Slot (ITL Slot)</h3>
<p>① 트랜잭션 ID ② 커밋 SCN
③ UBA &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ④ 커밋 Flag 
⑤ Locking 정보 (트랜잭션 진행을 위해 확보해야 Blocking되지x)</p>
<h3 id="lock-byte">Lock Byte</h3>
<p>: 해당 레코드를 누가 갱신 중인지?!
= 트랜잭션의 ITL 슬롯 번호 (포인터)</p>
<p>&nbsp;</p>
<hr>
<p>참고 도서 : 오라클 성능 고도화 원리와 해법 1</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQLP 3개월 독학 합격 후기 (51회)]]></title>
            <link>https://velog.io/@sua_ahn/SQLP-3%EA%B0%9C%EC%9B%94-%EB%8F%85%ED%95%99-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0-51%ED%9A%8C</link>
            <guid>https://velog.io/@sua_ahn/SQLP-3%EA%B0%9C%EC%9B%94-%EB%8F%85%ED%95%99-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0-51%ED%9A%8C</guid>
            <pubDate>Tue, 01 Oct 2024 06:09:22 GMT</pubDate>
            <description><![CDATA[<p>운좋게 SQLP에 붙었습니다!
이 글을 보시는 분들도 컴팩트하게 공부하고 한번에 붙기 바랍니다~</p>
<h4 id="-필자-스펙-참고">※ 필자 스펙 참고</h4>
<p>1년차 자바 개발자 🐣
작년 12월 sqld 취득 (1달 노랭이, 기출 독학)</p>
<hr>
<h3 id="공부-환경">공부 환경</h3>
<p>업무를 끝내고 근무시간 중 공부가 가능했으며
오전 근무 전, 점심시간에도 조금씩 투자했다.</p>
<p>오라클 실습은 회사에서 했으나, 개인적으로 실습해보고 싶은 분들은 도커 오라클을 사용하는 것을 추천한다!</p>
<p>※ 참고 사이트
<a href="https://stricky.tistory.com/395">윈도우 도커(docker) 오라클(oracle) 설치 하기 (1)</a></p>
<h3 id="학습-도서">학습 도서</h3>
<p><a href="https://search.shopping.naver.com/book/catalog/32442690254?cat_id=50010586&amp;frm=PBOKPRO&amp;query=%EC%B9%9C%EC%A0%88%ED%95%9C+sql+%ED%8A%9C%EB%8B%9D&amp;NaPm=ct%3Dm1a3wj94%7Cci%3D8bf009b6b04a0373ff1869b88f393f154f39e7ef%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3De9090c153dcc61d547118000c4bc3a0b870766e4">친절한 SQL 튜닝</a> (기본서)
<a href="https://search.shopping.naver.com/book/catalog/49411201618?cat_id=50010920&amp;frm=PBOKPRO&amp;query=SQLP+%EC%9E%90%EA%B2%A9%EA%B2%80%EC%A0%95+%ED%95%B5%EC%8B%AC%EB%85%B8%ED%8A%B8&amp;NaPm=ct%3Dm1a3xpow%7Cci%3D6a6ad54f41b65819c686a2130348fabbaa54e309%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D0f71ec120f95ec6fee4e357b8766fb6d0da63052">2024 국가공인 SQLP 자격검정 핵심노트 1, 2</a> (구매 추천)
<a href="https://search.shopping.naver.com/book/catalog/34372829619?cat_id=50010921&amp;frm=PBOKPRO&amp;query=%EC%98%A4%EB%9D%BC%ED%81%B4+%EC%84%B1%EB%8A%A5+%EA%B3%A0%EB%8F%84%ED%99%94+%EC%9B%90%EB%A6%AC%EC%99%80+%ED%95%B4%EB%B2%95&amp;NaPm=ct%3Dm1a3yjs8%7Cci%3D95edbb6a536845f67cb1de0bbd93c7cdd1914de5%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D08661fbcf7c9b60610d2f92586671295eed248bb">오라클 성능 고도화 원리와 해법 1, 2</a> (도서관 대출 추천)
<a href="https://search.shopping.naver.com/book/catalog/46592111623?cat_id=50010586&amp;frm=PBOKPRO&amp;query=%EA%B5%AD%EA%B0%80%EA%B3%B5%EC%9D%B8+SQLD+%EC%9E%90%EA%B2%A9%EA%B2%80%EC%A0%95+%ED%95%B5%EC%8B%AC%EB%85%B8%ED%8A%B8&amp;NaPm=ct%3Dm1a3z65s%7Cci%3Dcb4de1e79183237b0359e8089929f5c1eabbdc19%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3D79f9d5424f420d13b7c8f941c3faa027192483b1">2024 국가공인 SQLD 자격검정 핵심노트</a> (도서관 대출 추천)</p>
<p>+<a href="http://www.gurubee.net/article/87748">구루비 사이트</a> (책 요약돼있음)</p>
<hr>
<h3 id="공부-방법">공부 방법</h3>
<ol>
<li><p><strong>기본서 1회 정독 (3주)</strong></p>
<ul>
<li>실행계획과 트레이스 실습 안해본 분은 실습 먼저 해볼 것! (실습 방법은 기본서 뒤쪽에 나와있음)</li>
</ul>
</li>
<li><p><strong>핵심노트 1, 2 풀기 (5주)</strong></p>
<ul>
<li>문제가 너무 어려워서 다 틀려도 일단 풀면서 해설로 공부</li>
<li>다시 보면 좋을 문제 체크해놓기</li>
</ul>
</li>
<li><p><strong>핵심노트 다시 풀기 (2주)</strong></p>
<ul>
<li>동시에 오라클 성능 고도화 1 공부 <a href="https://velog.io/@sua_ahn/%EC%98%A4%EB%9D%BC%ED%81%B4-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-1">(포스팅 참고)</a></li>
</ul>
</li>
<li><p><strong>핵심노트 어려웠던 문제 다시 풀어보기 (1~2주)</strong></p>
<ul>
<li>오라클 성능 고도화 2 부분 참고</li>
</ul>
</li>
<li><p><strong>1, 2과목 복습으로 SQLD 핵심노트 가볍게 풀어보기 (5시간)</strong></p>
</li>
<li><p><strong>시험 직전 준비</strong></p>
<ul>
<li>헷갈리는 개념 정리 (정규형, 힌트 등)</li>
<li>튜닝 문제 유형화</li>
<li>SQLP 핵심노트 내 모의고사 풀기</li>
</ul>
</li>
</ol>
<hr>
<h3 id="기출문제">기출문제</h3>
<p>SQLP 공부를 시작하면서 가장 당황스러웠던 점이 인터넷 상에 기출문제가 거의 없다는 점이었다. 
그래서 핵심노트 문제를 여러번 풀고 아래 사이트들을 참고했다. 
사실 개념만 잘 잡혀있으면 기출이 필요없긴 하다.</p>
<p><a href="https://cafe.naver.com/sqlpd">데이터 전문가 포럼</a>
<a href="https://cafe.naver.com/dbian">DBian 포럼</a>
<a href="https://quizeey.com/sqlp-professional">2013년 기출문제</a></p>
<h3 id="51회-후기">51회 후기</h3>
<p>정규식 문제가 3개 정도 나왔으나 다 찍었다. 틀리라고 낸 문제같은 느낌,,,
그외에는 나올만한 것들이 나왔다.
1, 2과목은 SQLD 시험과 중복되는 문제도 있었다.</p>
<p>실기 2문제 다 실행계획을 보고 원본 쿼리를 작성하는 문제였다. 전혀 예상하지 못한 문제 유형이었고, 실행계획을 외운 적이 없어서 조금 당황했다. 그런데 다행히도 부분점수를 꽤 후하게 주셨다ㅜㅜ
<a href="https://m.cafe.naver.com/sqlpd/81493">https://m.cafe.naver.com/sqlpd/81493</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 파사드 패턴 Facade Pattern in Java]]></title>
            <link>https://velog.io/@sua_ahn/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%8C%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4-Facade-Pattern-in-Java</link>
            <guid>https://velog.io/@sua_ahn/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%8C%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4-Facade-Pattern-in-Java</guid>
            <pubDate>Fri, 24 May 2024 07:45:52 GMT</pubDate>
            <description><![CDATA[<h1 id="파사드-패턴">파사드 패턴</h1>
<p>: 복잡한 집합에 대한 <strong>단순화된 인터페이스</strong>를 제공하는 구조적 디자인 패턴</p>
<ul>
<li>복잡한 하위 시스템을 분리하여 숨기고(캡슐화) 하나의 고수준 인터페이스로 묶어줌</li>
</ul>
<blockquote>
<p><strong>Facade</strong>
정면; 건물의 정면 외관;</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/13942c43-c07c-452f-93c8-59f0ba8bd0de/image.png" alt=""></p>
<ul>
<li>디자인 패턴의 목적에 따라 3가지로 분류 가능
→ 구조 패턴에 속함<blockquote>
<p>구조 패턴
: 구조를 유연하고 효율적으로 유지하면서 객체들을 조립하는 방법</p>
</blockquote>
</li>
</ul>
<p>&nbsp;</p>
<hr>
<h2 id="java-예시1">Java 예시1</h2>
<p>연주회 콘서트를 구현</p>
<h3 id="subsystem-class">Subsystem class</h3>
<h4 id="연주자-객체">연주자 객체</h4>
<pre><code class="language-java">public class Player {
    private String instrument;

    public Player(String instrument) {
        this.instrument = instrument;
    }

    public void play() {
        prepare();
        playing();
        end();
    }

    private void prepare() {
        System.out.println(instrument + &quot; 연주 준비 중입니다.&quot;);
    }

    private void playing() {
        System.out.println(instrument + &quot; 연주 하고 있습니다.&quot;);
    }

    private void end() {
        System.out.println(instrument + &quot; 연주를 마쳤습니다.&quot;);
    }
}</code></pre>
<blockquote>
<p>cf) 객체의 자율성
객체가 가지고 있는 역할 또는 책임을 수행할 때, 해당 역할을 어떻게 수행할 지는 객체가 결정하게끔 해야한다.</p>
</blockquote>
<h4 id="지휘자-객체">지휘자 객체</h4>
<pre><code class="language-java">public class Conductor {

    public Conductor() {
        System.out.println(&quot;지휘자 입장&quot;);
    }

    public void start() {
        System.out.println(&quot;곡이 시작되었습니다.&quot;);
    }

    public void end() {
        System.out.println(&quot;곡이 끝났습니다.&quot;);
    }
}</code></pre>
<p>&nbsp;</p>
<h3 id="facade-class">Facade class</h3>
<p>서브클래스들을 감싸는 하나의 파사드 클래스를 정의</p>
<pre><code class="language-java">public class SpringConcert {

    public void start() {
        System.out.println(&quot;봄 콘서트 시작합니다.&quot;);

        Conductor conductor = new Conductor();
        conductor.start();

        Player player = new Player(&quot;바이올린&quot;);
        player.play();

        conductor.end();        
        System.out.println(&quot;봄 콘서트 끝났습니다.&quot;);

    }
}</code></pre>
<h3 id="실행">실행</h3>
<p>서브시스템들을 한번에 수행</p>
<pre><code class="language-java">public class Run {

    public static void main(String[] args) {
        new SpringConcert().start();
    }

}</code></pre>
<p>&nbsp;</p>
<hr>
<h2 id="layered-architecture-예시2">Layered Architecture 예시2</h2>
<p>Layered Architecture에서 <strong>Service Layer의 순환참조</strong>를 방지하기 위해 퍼사드 패턴을 적용할 수 있다.</p>
<p>비즈니스 로직이 복잡해지면 한 Service가 다른 Service를 의존하는 경우가 많아지게 된다. 그럼 의존관계가 얽히면서 순환참조가 발생하고, 서로 Bean을 주입받지 못해 Bean을 생성할 수 없게 된다.</p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/96517705-64af-4806-bcd1-ae72d31c975a/image.png" alt=""></p>
<p>따라서, Service Layer를 상위 계층(Component Service)과 하위 계층(Module Service)으로 구분하고, 의존성 방향을 한 방향으로 고정하여 순환참조를 방지할 수 있다.</p>
<h3 id="하위-계층">하위 계층</h3>
<p>Module Service는 독립적으로 사용 가능하기 때문에, 필요에 따라 재사용이 가능하다.</p>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class Module1ServiceImpl implements Module1Service {

    private final Test1Mapper test1Mapper;

    ...
}


@Service
@RequiredArgsConstructor
public class Module2ServiceImpl implements Module2Service {

    private final Test2Mapper test2Mapper;

    ...
}</code></pre>
<h3 id="상위-계층">상위 계층</h3>
<p>Component Service에 트랜잭션을 적용하여 여러 기능을 하나의 트랜잭션 단위에서 동작 가능하도록 할 수 있다.</p>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class ComponentServiceImpl implements ComponentService {

    private final Module1Service module1Service;
    private final Module2Service module2Service;

    ...
}</code></pre>
<p>&nbsp;</p>
<hr>
<p>참고사이트
<a href="https://refactoring.guru/ko/design-patterns/facade">https://refactoring.guru/ko/design-patterns/facade</a>
<a href="https://jangjjolkit.tistory.com/62">https://jangjjolkit.tistory.com/62</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MySQL, MariaDB] Lock wait timeout exceeded 에러]]></title>
            <link>https://velog.io/@sua_ahn/MySQL-Maria-Lock-wait-timeout-exceeded-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@sua_ahn/MySQL-Maria-Lock-wait-timeout-exceeded-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 17 May 2024 06:33:45 GMT</pubDate>
            <description><![CDATA[<p>Spring Boot 서비스 운영중 다음과 같은 에러가 발생하며, SQL문이 실행되지 않았다.</p>
<pre><code class="language-shell">### Error updating database.  Cause: java.sql.SQLException: (conn=1666666) Lock wait timeout exceeded; try restarting transaction
### The error may exist in class path resource [mappers/Mapper.xml]
### The error may involve com.test.sample.mappers.Mapper.updateColumns-Inline
### The error occurred while setting parameters
### SQL: UPDATE SAMPLETABLE   SET    UPD_ID = ?, UPD_DT = current_timestamp() WHERE ID = ?
### Cause: java.sql.SQLException: (conn=1666666) Lock wait timeout exceeded; try restarting transaction
; (conn=1666666) Lock wait timeout exceeded; try restarting transaction; nested exception is java.sql.SQLException: (conn=1666666) Lock wait timeout exceeded; try restarting transaction
</code></pre>
<p>위 SQL문을 수행하기 위해 lock이 필요하여 기다리던 중 타임아웃이 발생했다는 뜻!</p>
<p>트랜잭션이 할당된 제한 시간 내에 행 또는 테이블에 잠금을 얻지 못할 때 발생한다. 동시에 같은 데이터에 액세스하는 여러 트랜잭션이 있는 <strong>다중 사용자 환경</strong>에서 발생할 수 있다.</p>
<p>아래 쿼리문을 실행하면 아래처럼 제한 시간을 확인할 수 있다.
현재 InnoDB를 사용중이라면 <code>innodb_lock_wait_timeout</code>의 default 값인 <strong>50sec</strong>만에 타임아웃이 일어나게 된다.</p>
<p><em>*DB 엔진 확인 방법은 맨 아래에 있음</em></p>
<pre><code class="language-sql">SHOW VARIABLES LIKE &#39;%timeout&#39;;</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/bf3d216b-7f56-41f0-ab3f-0327a16d0a6f/image.png" alt=""></p>
<p>&nbsp;</p>
<hr>
<h2 id="timeout이-발생하는-원인과-해결방법">Timeout이 발생하는 원인과 해결방법</h2>
<p>&nbsp;</p>
<h3 id="1-lock-wait-timeout-값이-작게-설정된-경우">1. lock wait timeout 값이 작게 설정된 경우</h3>
<p>lock wait timeout 값을 default 값보다 작게 설정해놓았을 경우 
혹은 lock wait timeout 값에 비해 트랜잭션이 긴 경우</p>
<h4 id="lock-wait-timeout-값-증가">lock wait timeout 값 증가</h4>
<pre><code class="language-sql">-- SUPER 권한 필요
SET GLOBAL innodb_lock_wait_timeout = 300; -- 300초(5분)로 설정

SET SESSION lock_wait_timeout = 0;    -- no limit</code></pre>
<p>다만, 잠금 대기시간 값을 증가시키면 트랜잭션의 대기 시간이 길어져 성능 문제가 발생할 수 있으므로 주의!</p>
<p>&nbsp;</p>
<h3 id="2-transaction-수행시간이-긴-경우-✨">2. Transaction 수행시간이 긴 경우 ✨</h3>
<p>수행시간이 긴 트랜잭션이 lock을 얻고 오랫동안 lock을 풀지 않으면, 동일한 lock을 얻고자 한 다른 트랜잭션은 타임아웃이 발생한다.</p>
<p>또는 처음에는 데이터가 적어서 문제가 되지 않다가 시간이 지나면서 데이터가 쌓여 문제 되는 경우가 종종 있다.</p>
<p>트랜잭션을 가능한 짧게 유지하여 충돌의 가능성을 줄이는 것이 좋다.</p>
<blockquote>
<p>Transaction은 더 이상 쪼갤 수 없는 <strong>최소 작업 단위</strong>가 되어야 한다. 문제가 발생했을 경우 이전 상태로 rollback 할 수 있도록 설계한다.</p>
</blockquote>
<p>&nbsp;</p>
<h4 id="응용-프로그램-로직-검토">응용 프로그램 로직 검토</h4>
<p>트랜잭션을 올바르게 관리하고 리소스를 적절히 해제하는지 코드를 검토해볼 필요가 있다.</p>
<h4 id="쿼리-최적화">쿼리 최적화</h4>
<p>쿼리 튜닝, 테이블 스페이스 사용으로 쿼리 실행시간을 줄임으로써 잠금경합을 감소시킬 수 있다.</p>
<h4 id="명시적-잠금-사용">명시적 잠금 사용</h4>
<p>기본 잠금 동작에 의존하는 대신 명시적 잠금을 사용하여 잠금이 언제 어떻게 획득되는지 제어할 수 있다.</p>
<blockquote>
<p><strong>내가 겪은 문제상황</strong>
Spring에서 TransactionManager(@Transactional 역할)를 사용하며, impl단에서 여러 쿼리를 실행하는 메소드가 하나의 트랜잭션으로 수행되는 웹서비스를 운영중이었다.</p>
</blockquote>
<p>이때, [사용자가 몰림 + 다른 배치 프로그램에서 동일한 DB 사용 + 몇 년간 운영하며 데이터가 많이 적재됨]으로 여러 상황이 겹치면서 해당 에러가 발생했었다.</p>
<blockquote>
</blockquote>
<p>운영중이었기 때문에 대응 방법에 제약이 있었고, 우선은 임시로 다음과 같이 대처했다. 트랜잭션 수행시간 감소를 위해 쿼리를 튜닝하였고, 데이터 백업을 계획하였다. 또, 배치 프로그램 실행과 웹서비스 사용이 동시에 집중되지 않고 적절히 분배되도록 프로그램에 제한을 걸었다. 장기적으로는 프로그램 버전업을 기획 중이다.</p>
<p>&nbsp;</p>
<h3 id="3-isolation-level이-문제인-경우">3. Isolation level이 문제인 경우</h3>
<pre><code class="language-sql">-- 현재 DB의 격리레벨 조회 : 기본적으로 REPEATABLE-READ
-- MySQL 5.7.2 이하
SELECT @@GLOBAL.tx_isolation;

-- MySQL 8.0 이상
SELECT @@GLOBAL.transaction_isolation;</code></pre>
<p>Isolation Level은 Transaction에서 <strong>일관성 없는 데이터를 허용하는 수준</strong>에 대한 설정을 의미한다.</p>
<ul>
<li><p>Read Uncommitted 
: Transaction이 끝나지 않은 상황에서 다른 Transaction의 변경사항에 대한 조회가 가능하다.</p>
</li>
<li><p>Read Committed 
: Transaction이 끝나지 않은 데이터에 대한 조회의 경우 Shared Lock이 발생하며, Commit된 데이터만 조회가 가능하다.</p>
</li>
<li><p>Repeatable Read 
: Transaction의 Id를 기준으로 생성된 Snapshot 범위 내에서 데이터를 조회하기 떄문에 내용이 항상 동일함을 보장해준다.</p>
</li>
<li><p>Serializable 
: 가장 엄격한 Isolation Level로 모든 Transaction을 직렬화하여 처리한다.</p>
</li>
</ul>
<p>Timeout Exceed가 발생 가능한 가장 유력한 상황은 위에서 <strong>Repeatable Read</strong>이다.</p>
<p>Repeatable Read는 Transaction의 첫 SELECT에서 해당 데이터에 <strong>Shared Lock</strong>을 걸고 데이터의 Shapshot을 생성 및 기록한다.</p>
<p>이후 동일 Transaction 내의 SELECT는 Shanpshot에서 읽게 된다.</p>
<p>Transaction에서 Select를 해서 <strong>snapshot을 만드는 시간</strong>이 오래 걸려서 timeout 설정값을 넘기게 되면 Lock Wait Timeout Exceeded가 발생하게 된다.</p>
<blockquote>
<p>Default Level</p>
</blockquote>
<ul>
<li>MySQL : Repeatable Read</li>
<li>Oracle : Read Committed</li>
</ul>
<p>&nbsp;</p>
<h3 id="4-비정상-종료로-잠김">4. 비정상 종료로 잠김</h3>
<p>프로세스 목록을 조회하여 오랫동안 잠금을 갖고있는 프로세스 강제종료</p>
<pre><code class="language-sql">-- 프로세스 목록 조회
SHOW PROCESSLIST;

-- PROCESS 권한 필요
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
SELECT * FROM information_schema.INNODB_TRX;

-- 프로세스 확인
SELECT * FROM information_schema.processlist where id = 175;    

-- 프로세스 강제종료
kill 175;</code></pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h4 id="cf-db-엔진-확인-방법">cf) DB 엔진 확인 방법</h4>
<pre><code class="language-sql">SELECT engine 
FROM information_schema.TABLES 
WHERE table_name=&#39;테이블명&#39; 
    AND table_schema=&#39;디비명&#39;;
-- 또는
SHOW TABLE STATUS WHERE name=&#39;테이블명&#39;;
</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/a229d64c-5088-46ac-a642-98a8ee783721/image.png" alt=""></p>
<p>&nbsp;</p>
<hr>
<p>*참고사이트
<a href="https://jaenjoy.tistory.com/26">https://jaenjoy.tistory.com/26</a>
<a href="https://small-dbtalk.blogspot.com/2015/01/lockwaittimeout-default_9.html">https://small-dbtalk.blogspot.com/2015/01/lockwaittimeout-default_9.html</a>
<a href="https://adbancedteam.tistory.com/223">https://adbancedteam.tistory.com/223</a>
<a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-transactions.html">https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-transactions.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Apache SSL 인증서 교체 방법]]></title>
            <link>https://velog.io/@sua_ahn/Apache-SSL-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EA%B5%90%EC%B2%B4-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@sua_ahn/Apache-SSL-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EA%B5%90%EC%B2%B4-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 10 May 2024 05:56:35 GMT</pubDate>
            <description><![CDATA[<p>인증서를 발급받으면 아래 4개의 파일을 받을 수 있고, 
1~2번 파일만 있어도 HTTPS 설정이 가능하다.
3, 4번 파일은 사용자의 브라우저에서 인증기관이 등록되어 있지 않을 때 필요한 파일이다. 혹시 모르니 같이 등록해놓자.</p>
<ol>
<li>도메인 인증서
cert.pem
server.crt
ssl.crt</li>
<li>개인키</li>
</ol>
<p>*.key
key.pem</p>
<ol start="3">
<li>Root CA 인증서
ca.pem
ca.crt</li>
<li>Chain CA 인증서 (sub CA)
ca.pem
ca.crt</li>
</ol>
<hr>
<blockquote>
<h4 id="cf-개인키-패스워드-제거">cf) 개인키 패스워드 제거</h4>
<p>apache 실행할 때 마다 패스워드를 입력하지 않기 위해 비밀번호 제거</p>
</blockquote>
<pre><code class="language-shell">openssl rsa -in prv.key –out prv_non.key</code></pre>
<h3 id="1-아파치-중지">1. 아파치 중지</h3>
<pre><code class="language-shell">#아파치 프로세스 확인
ps –elf | grep httpd

#아파치 중지
cd /home/webapp/httpd/bin
./apachectl stop</code></pre>
<p>&nbsp;</p>
<h3 id="2-아파치-설정-확인">2. 아파치 설정 확인</h3>
<h4 id="아파치-정보-확인">아파치 정보 확인</h4>
<pre><code class="language-shell">./httpd –V</code></pre>
<h4 id="아파치-설정파일-확인-및-수정">아파치 설정파일 확인 및 수정</h4>
<pre><code class="language-shell">#설정파일 수정
cd ../conf/extra
vim httpd-ssl.conf

---
SSLCertificateFile “/home/webapp/httpd/conf/ssl/cert.crt”
SSLCertificateKeyFile “/home/webapp/httpd/conf/ssl/prv.key”
SSLCertificateChainFile “/home/webapp/httpd/conf/ssl/subca.crt”
SSLCACertificateFile “/home/webapp/httpd/conf/ssl/rootca.crt”</code></pre>
<p><code>SSLCertificateChainFile</code> 옵션은 version 2.4.8 부터 deprecated
<code>SSLCACertificateFile</code> 옵션으로 통합 사용</p>
<h4 id="아파치-설정-체크">아파치 설정 체크</h4>
<pre><code class="language-shell">cd ../../bin
./apachectl configtest</code></pre>
<p>&nbsp;</p>
<h3 id="3-인증서-변경">3. 인증서 변경</h3>
<pre><code class="language-shell">cd ../conf

#기존 인증서 백업
mv ssl ssl_bak

#인증서 복사
cp -rp /tmp/ssl ./</code></pre>
<p>&nbsp;</p>
<h3 id="4-아파치-재실행">4. 아파치 재실행</h3>
<pre><code class="language-shell">#아파치 실행
./apache start

#개인키에 비밀번호가 설정되어 있으면 비밀번호 입력!</code></pre>
<p>&nbsp;</p>
<h3 id="5-인증서-변경-확인">5. 인증서 변경 확인</h3>
<pre><code class="language-shell">#실행 확인
ps -ef | grep httpd

#SSL 포트 확인
netstat –ant | grep 443

#SSL 인증서 정보 확인
echo | openssl s_client –showcerts –connect 127.0.0.1:443 2&gt;/dev/null | openssl x509 –inform pem –noout -text</code></pre>
<h4 id="브라우저-접속-확인-chrome">브라우저 접속 확인 (Chrome)</h4>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/bfec88bd-0076-4869-9b72-041a1313c962/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/7cfcedd8-605b-4035-ab57-736a7b72885d/image.png" alt=""></p>
<p>&nbsp;</p>
<hr>
<p>참고사이트
<a href="https://thekeystore.blogspot.com/2015/11/ssl.html">https://thekeystore.blogspot.com/2015/11/ssl.html</a>
<a href="https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile">https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IN 연산자 사용 시 FULL SCAN을 한다? (실행계획 분석)]]></title>
            <link>https://velog.io/@sua_ahn/IN-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%82%AC%EC%9A%A9-%EC%8B%9C-FULL-SCAN-%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@sua_ahn/IN-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%82%AC%EC%9A%A9-%EC%8B%9C-FULL-SCAN-%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Tue, 07 May 2024 07:12:20 GMT</pubDate>
            <description><![CDATA[<p>&nbsp;</p>
<h1 id="in-연산자-사용-시-full-scan-">IN 연산자 사용 시 FULL SCAN ?!</h1>
<p>Oracle 등 다른 DBMS에서는 10ms 내로 소요되는 UPDATE문이 
<strong>MySQL, MariaDB에서만 10,000~20,000ms 가량 소요</strong>되는 것이 확인되었다. (약 2만개의 레코드를 가진 테스트DB)</p>
<p>우선, 10.3 버전의 MariaDB에서 실행계획을 조회해보았다.
<strong>IN 연산자</strong>로 PK 혹은 인덱스 컬럼 조회 시 Unique 나 Range Index Scan을 하지 않고, <code>Index Full Scan</code>을 하는 것으로 나왔다.</p>
<p>원인과 해결 방법을 알아보자!</p>
<p>&nbsp;</p>
<hr>
<h1 id="실행계획-조회">실행계획 조회</h1>
<h2 id="1-mysql-mariadb">1. MySQL, MariaDB</h2>
<pre><code class="language-sql">-- 실행계획 조회
EXPLAIN 
UPDATE sample_table
SET UPD_DT = current_timestamp()
WHERE sample_id IN (SELECT sample_id 
                    FROM other_table 
                    WHERE other_id = 1000);

-- 실행계획 조회 (JSON 형식)
EXPLAIN FORMAT = JSON
UPDATE sample_table
SET UPD_DT = current_timestamp()
WHERE sample_id IN (SELECT sample_id 
                    FROM other_table 
                    WHERE other_id = 1000);</code></pre>
<h3 id="실행계획-조회-결과">실행계획 조회 결과</h3>
<pre><code class="language-sql">id|select_type       |table       |type          |possible_keys                |key    |key_len|ref |rows   |Extra      |
--+------------------+------------+--------------+-----------------------------+-------+-------+----+-------+-----------+
 1|PRIMARY           |SAMPLE_TABLE|index         |                             |PRIMARY|92     |    |1759872|Using where|
 2|DEPENDENT SUBQUERY|OTHER_TABLE |index_subquery|PRIMARY,PK_OTHER_TABLE,IDX_01|PRIMARY|92     |func|      1|Using where|</code></pre>
<p>아래 두 컬럼을 중점적으로 보자!</p>
<ul>
<li><p><code>type</code> 컬럼 : 테이블 접근 방식</p>
<ul>
<li><p>system : 테이블에 데이터가 없거나 한 개만 있는 경우</p>
</li>
<li><p>const : UNIQUE INDEX SCAN (조회되는 데이터가 단 1건일 때)</p>
</li>
<li><p>eq_ref : JOIN을 수행할 때 driven table에 고유인덱스나 기본키로 단 1건의 데이터를 조회하는 방식</p>
</li>
<li><p>ref : 동등 조건으로 조회 (반환되는 레코드가 반드시 1건이라는 보장이 없음)</p>
</li>
<li><p>range : 테이블 내의 연속된 데이터 범위를 조회</p>
</li>
<li><p><strong>index</strong> : INDEX FULL SCAN (인덱스는 보통 테이블보다 크기가 작으므로 테이블 풀 스캔 방식보다는 빠를 가능성이 있음)</p>
</li>
<li><p>index_subquery : IN (subquery) 형태의 쿼리를 위한 접근 방식으로, 인덱스를 이용해 중복값을 제거</p>
</li>
<li><p>unique_subquery : IN (subquery) 형태의 서브 쿼리에서 중복되지 않은 유니크한 값만 반환 (별도의 중복 제거 작업이 필요하지 않음)</p>
</li>
<li><p>ALL : TABLE FULL SCAN (전체 테이블 중 10~20% 이상 분량의 데이터를 조회할 때는 ALL 유형이 오히려 성능상 유리할 수 있음)</p>
</li>
</ul>
</li>
<li><p><code>rows</code> 컬럼 : 쿼리 수행에서 예상하는 검색해야 할 행수</p>
</li>
</ul>
<p>&nbsp;</p>
<h4 id="실행계획-조회-결과-json-형식">실행계획 조회 결과 (JSON 형식)</h4>
<p><code>access_type</code>, <code>rows</code>에 주목</p>
<pre><code>{
  &quot;query_block&quot;: {
    &quot;select_id&quot;: 1,
    &quot;table&quot;: {
      &quot;update&quot;: 1,
      &quot;table_name&quot;: &quot;SAMPLE_TABLE&quot;,
      &quot;access_type&quot;: &quot;index&quot;,
      &quot;key&quot;: &quot;PRIMARY&quot;,
      &quot;key_length&quot;: &quot;&quot;,
      &quot;used_key_parts&quot;: [&quot;SAMPLE_ID&quot;],
      &quot;rows&quot;: 1759872,
      &quot;attached_condition&quot;: &quot;&lt;in_optimizer&gt;(SAMPLE_TABLE.SAMPLE_ID,&lt;exists&gt;(subquery#2)) and SAMPLE_TABLE.PRCS_CD = &#39;S&#39;&quot;
    },
    &quot;subqueries&quot;: [
      {
        &quot;query_block&quot;: {
          &quot;select_id&quot;: 2,
          &quot;table&quot;: {
            &quot;table_name&quot;: &quot;OTHER_TABLE&quot;,
            &quot;access_type&quot;: &quot;index_subquery&quot;,
            &quot;possible_keys&quot;: [&quot;PRIMARY&quot;, &quot;PK_OTHER_TABLE&quot;, &quot;IDX_01&quot;],
            &quot;key&quot;: &quot;PRIMARY&quot;,
            &quot;key_length&quot;: &quot;92&quot;,
            &quot;used_key_parts&quot;: [&quot;SAMPLE_ID&quot;],
            &quot;ref&quot;: [&quot;func&quot;],
            &quot;rows&quot;: 1,
            &quot;filtered&quot;: 100
          }
        }
      }
    ]
  }
}</code></pre><p>&nbsp;</p>
<hr>
<h2 id="2-비슷한-쿼리와-비교">2. 비슷한 쿼리와 비교</h2>
<h3 id="서브쿼리-대신-값을-넣은-update문">서브쿼리 대신 값을 넣은 UPDATE문</h3>
<ul>
<li>range : 테이블 내의 연속된 데이터 범위를 조회<pre><code class="language-sql">EXPLAIN 
UPDATE sample_table
SET UPD_DT = current_timestamp()
WHERE sample_id IN (&#39;a&#39;, &#39;b&#39;, &#39;c&#39;);

</code></pre>
</li>
</ul>
<p>-- result
id|select_type|table        |type |possible_keys           |key    |key_len|ref|rows|Extra      |
--+-----------+-------------+-----+------------------------+-------+-------+---+----+-----------+
 1|SIMPLE     |SAMPLE_TABLE |range|PRIMARY,PK_SAMPLE_TABLE |PRIMARY|92     |   |   2|Using where|</p>
<pre><code>IN절 안에 값을 명시했을 경우, **인덱스를 정상적으로 사용했음**을 확인할 수 있다.

&gt;&#39;인덱스를 정상적으로 사용한다&#39;의 의미는
리프 블록에서 스캔 시작점을 찾아 스캔하다가 중간에 멈추는 것을 의미한다. 즉, 리프 블록 일부만 스캔하는 Index Range Scan을 의미한다.

&amp;nbsp;

### where절 조건이 같은 SELECT문
  - eq_ref : 조인 수행 시, 인덱스로 단 1건의 데이터를 조회
```sql
EXPLAIN 
SELECT sample_id, upd_dt
FROM sample_table
WHERE sample_id IN (SELECT sample_id 
                    FROM other_table 
                    WHERE other_id = 1000);


-- result
id|select_type |table        |type  |possible_keys                |key    |key_len|ref                       |rows|Extra      |
--+------------+-------------+------+-------------------------------------+-------+--------------------------+----+-----------+
 1|PRIMARY     |&lt;subquery2&gt;  |ALL   |distinct_key                 |       |       |                          |   2|           |
 1|PRIMARY     |SAMPLE_TABLE |eq_ref|PRIMARY,PK_SAMPLE            |PRIMARY|92     |SCH01.OTHER_TABLE.OTHER_ID|   1|           |
 2|MATERIALIZED|OTHER_TABLE  |ref   |PRIMARY,PK_OTHER_TABLE,IDX_01|IDX_01 |4      |const                     |   2|Using index|</code></pre><p><strong>같은 조건임에도 UPDATE문에서는 <code>index</code>, SELECT문에서는 <code>eq_ref</code> 로 테이블에 접근했다!</strong>
SELECT문에서는 하나의 데이터로 검색할 것을 예상했으나, UPDATE문에서는 그러지 못했을뿐만 아니라 인덱스 스캔 시작점조차 찾지 못했다.</p>
<p>해당 원인을 찾아보았으나, 찾지 못했다...
아마 옵티마이저의 문제같으나 근본적인 해결 방법을 찾기는 어려울 것 같아 다른 방법으로 해결하였다. (아래 해결 방법 확인!)</p>
<p>&nbsp;</p>
<hr>
<h2 id="3-oracle과-비교">3. Oracle과 비교</h2>
<pre><code class="language-sql">-- 실행계획 저장
EXPLAIN PLAN FOR
UPDATE sample_table
SET upd_dt = sysdate
WHERE sample_id IN (SELECT sample_id 
                    FROM other_table 
                    WHERE other_id = 1000)
    AND prcs_cd = &#39;S&#39;;

-- 실행계획 조회
SELECT * FROM TABLE(dbms_xplan.display);</code></pre>
<h3 id="실행계획-조회-결과-1">실행계획 조회 결과</h3>
<p>Id 6번을 보면 PK를 이용하여 <code>INDEX UNIQUE SCAN</code>을 한다.
(수직적 탐색만으로 데이터를 찾는 스캔 방식으로서, 
Unique 인덱스를 &#39;=&#39; 조건으로 탐색하는 경우에 작동한다.)</p>
<pre><code class="language-sql">PLAN_TABLE_OUTPUT                                                                                    |
-----------------------------------------------------------------------------------------------------+
Plan hash value: 3021195589                                                                          |
-----------------------------------------------------------------------------------------------------|
| Id  | Operation                     | Name                | Rows  | Bytes | Cost (%CPU)| Time     ||
-----------------------------------------------------------------------------------------------------|
|   0 | UPDATE STATEMENT              |                     |   950 | 68400 |   963   (1)| 00:00:12 ||
|   1 |  UPDATE                       | SAMPLE_TABLE        |       |       |            |          ||
|   2 |   NESTED LOOPS                |                     |       |       |            |          ||
|   3 |    NESTED LOOPS               |                     |   950 | 68400 |   963   (1)| 00:00:12 ||
|   4 |     SORT UNIQUE               |                     |   950 | 25650 |    12   (0)| 00:00:01 ||
|*  5 |      INDEX RANGE SCAN         | IDX_01              |   950 | 25650 |    12   (0)| 00:00:01 ||
|*  6 |     INDEX UNIQUE SCAN         | PK_SAMPLE_TABLE     |     1 |       |     1   (0)| 00:00:01 ||
|*  7 |    TABLE ACCESS BY INDEX ROWID| SAMPLE_TABLE        |     1 |    45 |     2   (0)| 00:00:01 ||
-----------------------------------------------------------------------------------------------------|
Predicate Information (identified by operation id):                                                  |
---------------------------------------------------                                                  |
   5 - access(&quot;OTHER_ID&quot;=1000)                                                                       |
   6 - access(&quot;SAMPLE_ID&quot;=&quot;SAMPLE_ID&quot;)                                                               |
   7 - filter(&quot;PRCS_CD&quot;=&#39;S&#39;)                                                                         |</code></pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr>
<h1 id="index-full-scan-원인">Index Full Scan 원인</h1>
<p>샘플 쿼리문은 <strong>특수한 경우</strong>로 판단되어 일반적인 경우도 추가로 알아보았다.</p>
<p>일반적으로 Index Full Scan을 했다는 것은 인덱스를 정상적으로 사용하지 못했다고 해석할 수 있다. 즉, <strong>인덱스 스캔 시작점</strong>을 찾지 못해 Index Range Scan을 하지 못했다는 의미이다.</p>
<p>그 이유는 다음과 같다.</p>
<ol>
<li>IN에 포함되는 데이터가 너무 많은 경우</li>
<li>IN에 포함되는 데이터가 흩어져 있는 경우</li>
<li>가공값으로 검색</li>
</ol>
<blockquote>
<p>IN 조건절을 사용하지 않고 Index Full Scan하는 경우</p>
</blockquote>
<ul>
<li>Like로 중간에 포함된 값을 검색</li>
<li>여러 컬럼에 대한 OR조건</li>
</ul>
<p>&nbsp;</p>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="1-range_optimizer_max_mem_size-조절">1. range_optimizer_max_mem_size 조절</h3>
<p><code>range_optimizer_max_mem_size</code>는 range optimizer로 이용할 수 있는 메모리 크기를 조절하는 시스템 변수이다.
IN에 포함된 데이터의 비율이 매우 높다면 range 함수 대신 다른 방법으로 데이터를 탐색한다. 
range 함수를 이용할 수 있도록 range_optimizer_max_mem_size를 늘리거나 0으로 설정해 볼 수 있다.</p>
<ul>
<li>MySQL default 값<ul>
<li>ver 5.7.12 이상 : 8M</li>
<li>ver 5.7.11 이하 : 1.5M</li>
</ul>
</li>
</ul>
<p>참고로, Mysql 5.7버전 이상부터는 0으로 설정 시 unlimit을 의미한다.</p>
<p>아래 방법으로 해당 변수를 임의로 설정할 수 있다.</p>
<pre><code class="language-sql"># range_optimizer_max_mem_size 확인
SHOW VARIABLES LIKE &#39;range_optimizer%&#39;;

+------------------------------+---------+
| Variable_name                | Value   |
+------------------------------+---------+
| range_optimizer_max_mem_size | 8388608 |
+------------------------------+---------+

# 글로벌 적용 &amp; 메모리 제한 없음 (no limit)
SET GLOBAL range_optimizer_max_mem_size = 0;

# 글로벌 적용 &amp; 5000000 bytes 로 메모리 제한
SET range_optimizer_max_mem_size = 5000000;</code></pre>
<ul>
<li><p>MySQL 5.7 에서 추가된 range_optimizer_max_mem_size
<a href="https://blog.naver.com/parkjy76/222450202444">https://blog.naver.com/parkjy76/222450202444</a></p>
</li>
<li><p>MariaDB 10.3 과 MySQL 5.7의 시스템변수 차이
<a href="https://mariadb.com/kb/en/system-variable-differences-between-mariadb-10-3-and-mysql-5-7/">https://mariadb.com/kb/en/system-variable-differences-between-mariadb-10-3-and-mysql-5-7/</a></p>
</li>
<li><p>MariaDB does not limit memory used for range optimization
<a href="https://jira.mariadb.org/browse/MDEV-9764?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&amp;showAll=true">https://jira.mariadb.org/browse/MDEV-9764?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&amp;showAll=true</a></p>
</li>
<li><p>예시
<a href="https://hoing.io/archives/24493">https://hoing.io/archives/24493</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h3 id="2-조건-범위-줄이기">2. 조건 범위 줄이기</h3>
<p><code>range_optimizer_max_mem_size</code>를 충분한 크기로 조절해도 (혹은 0으로 설정해도) 인덱스를 타지 않을 수 있다. </p>
<p>이는 조건에 일치하는 레코드가 너무 많거나, 데이터가 인덱스 전 범위에 걸쳐 흩어져 있어 MySQL 옵티마이저가 range scan을 포기하고 full scan을 하는 것일 수 있다. </p>
<p>IN에 포함되는 데이터 자체를 줄이거나, 조건 범위를 잘라서 여러번 조회함으로써 해결해볼 수 있을 것이다.</p>
<ul>
<li>참고
<a href="https://jaejade.tistory.com/128">https://jaejade.tistory.com/128</a></li>
</ul>
<p>&nbsp;</p>
<h3 id="3-쿼리-튜닝-✨">3. 쿼리 튜닝 ✨</h3>
<p>샘플 쿼리의 경우 가능한 튜닝 방법은 아래와 같다.</p>
<p>1) 다른 연산자 사용 (ex. &#39;=&#39;)
2) 인덱스를 타도록 IN구문 앞에 다른 인덱스 조건을 넣기</p>
<p>(실제로는 위 2번 방법을 사용하여 해결하였다!)</p>
<p>그 외 테이블과 데이터 등에 따라 튜닝 방법은 다양하므로, 상황에 맞게 쿼리를 튜닝해보자</p>
<blockquote>
<p><strong>DBeaver 쿼리 수행이력(소요시간) 조회 방법</strong>
상단 시계 아이콘 - 트랜잭션 로그 또는 쿼리 관리자
<img src="https://velog.velcdn.com/images/sua_ahn/post/330049dd-92d0-4b7d-b6d1-e6ad02884fa9/image.png" alt=""></p>
</blockquote>
<p>&nbsp;</p>
<hr>
<h3 id="cf-통계테이블-갱신">cf) 통계테이블 갱신</h3>
<p>실행계획 조회 기능은 통계정보 테이블을 기반으로 동작하므로, 통계정보가 부족하거나 불일치할 경우 분석의 정확성이 다소 떨어질 수 있다. 따라서, 아래와 같이 통계테이블을 갱신해보자.</p>
<h4 id="mysql-mariadb">MySQL, MariaDB</h4>
<pre><code class="language-sql">ANALYZE TABLE sample_table</code></pre>
<pre><code>Table              |Op     |Msg_type|Msg_text|
-------------------+-------+--------+--------+
SCHEMA.SAMPLE_TABLE|analyze|status  |OK      |</code></pre><blockquote>
<h4 id="통계테이블-갱신--실행-계획-조회-동시-실행">통계테이블 갱신 + 실행 계획 조회 동시 실행</h4>
<p>MySQL 8.0.18버전부터 가능</p>
</blockquote>
<pre><code class="language-sql">EXPLAIN ANALYZE 분석하고자하는 쿼리;</code></pre>
<hr>
<p>*참고사이트</p>
<ul>
<li>실행계획
<a href="https://yozm.wishket.com/magazine/detail/2260/">https://yozm.wishket.com/magazine/detail/2260/</a></li>
<li>인덱스 사용법
<a href="https://sewonzzang.tistory.com/37">https://sewonzzang.tistory.com/37</a>
<a href="https://goldfishhead.tistory.com/69">https://goldfishhead.tistory.com/69</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[여러 가상머신에 별도 IP를 할당해보자! (VirtualBox, Vagrant)]]></title>
            <link>https://velog.io/@sua_ahn/%EC%97%AC%EB%9F%AC-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0%EC%97%90-%EB%B3%84%EB%8F%84-IP%EB%A5%BC-%ED%95%A0%EB%8B%B9%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@sua_ahn/%EC%97%AC%EB%9F%AC-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0%EC%97%90-%EB%B3%84%EB%8F%84-IP%EB%A5%BC-%ED%95%A0%EB%8B%B9%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 05 Apr 2024 07:50:00 GMT</pubDate>
            <description><![CDATA[<h1 id="vagrant를-통한-virtualbox-구성">Vagrant를 통한 VirtualBox 구성</h1>
<h2 id="vagrant">Vagrant</h2>
<p>: 복잡한 <strong>VM 관리</strong>를 도와주는 오픈소스 utility</p>
<h3 id="vagrantfile">Vagrantfile</h3>
<p>: VM configuration file로, <strong>VM에 필요한 것들을 정의</strong>해 놓으면 Vagrant가 자동으로 VM에 환경을 구성함</p>
<p>→ 해당 파일에서 가상 서버의 IP를 설정할 수 있음! 
<img src="https://velog.velcdn.com/images/sua_ahn/post/5f3bae83-680a-45d6-897c-f04d8f947a86/image.png" alt=""></p>
<p>&nbsp;</p>
<hr>
<h2 id="1-설치-및-초기화">1. 설치 및 초기화</h2>
<h3 id="설치파일-버전">설치파일 버전</h3>
<p>아래 버전보다 버전업 시 연동 안될 수 있음</p>
<ul>
<li>VirtualBox : 6.1.50</li>
<li>Vagrant : 2.2.19</li>
<li>tabby(윈도우 터미널 프로그램) : 1.0.207
<img src="https://velog.velcdn.com/images/sua_ahn/post/1de8b61f-1742-4f54-a1e2-7fa08cf3f015/image.png" alt="">
&nbsp;</li>
</ul>
<h3 id="플러그인-설치-및-초기화">플러그인 설치 및 초기화</h3>
<p>vagrant-vbguest 설치
(VirtualBox의 그래픽 개선 프로그램인 Guest Additions를 최신상태로 유지시켜주는 플러그인)</p>
<p>→ Vagrant를 초기화하여 Vagrantfile 생성</p>
<pre><code class="language-shell">&gt;&gt; vagrant plugin install vagrant-vbguest --plugin-version 0.21

&gt;&gt; vagrant init</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/46c88184-1776-4fe9-a047-314a2fce938b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/114e6aa0-c4f4-4e9b-b915-6cc9723ada5f/image.png" alt=""></p>
<p>&nbsp;</p>
<h2 id="2-vagrantfile-수정">2. Vagrantfile 수정</h2>
<p>맨 위 두 줄은 Vagrantfile이 ruby 언어를 사용한다는 것을 알려주는 글이므로 지우면 안되고, 나머지 주석은 삭제한다.</p>
<p>Vagrant는 <a href="https://app.vagrantup.com/boxes/search">Vagrant cloud</a>를 통해 <strong>Vagrant Box</strong>==Virtual Machine Image(Ubuntu, Centos, etc)를 제공한다. 도커 허브에서 도커 이미지를 가져오는 것과 비슷하다. 기본적으로 박스는 <code>base</code>로 기본 설정이 되어있는데, 이 부분을 원하는 OS로 수정하여 설치한다.</p>
<pre><code class="language-ruby"># -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(&quot;2&quot;) do |config|

    config.vm.define &quot;was01&quot; do |was01|
      was01.vm.box=&quot;centos/7&quot;
      was01.vm.hostname=&quot;was01&quot;
      was01.vm.network &quot;private_network&quot;, ip: &quot;192.16.3.31&quot;
      was01.vm.synced_folder &quot;./data&quot;, &quot;/vagrant_data&quot;
   end

   config.vm.define &quot;web01&quot; do |web01|
      web01.vm.box=&quot;centos/7&quot;
      web01.vm.hostname=&quot;web01&quot;
      web01.vm.network &quot;private_network&quot;, ip: &quot;192.16.3.32&quot;
      web01.vm.synced_folder &quot;./data&quot;, &quot;/vagrant_data&quot;
   end

   config.vm.define &quot;db01&quot; do |db01|
      db01.vm.box=&quot;centos/7&quot;
      db01.vm.hostname=&quot;db01&quot;
      db01.vm.network &quot;private_network&quot;, ip: &quot;172.17.102.213&quot;
      db01.vm.synced_folder &quot;./data&quot;, &quot;/vagrant_data&quot;
   end

end</code></pre>
<p>→ 잘 저장되었는지 확인</p>
<pre><code class="language-shell">&gt;&gt; vagrant status</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/7a00dea3-3081-45b1-bf1e-f2bc0869c601/image.png" alt=""></p>
<p>&nbsp;</p>
<h2 id="3-가상-머신-생성">3. 가상 머신 생성</h2>
<pre><code class="language-shell">&gt;&gt; vagrant up was01
&gt;&gt; vagrant up db01</code></pre>
<p>OS가 설치되고 기타 dependencies도 설치되기 때문에 시간이 걸린다.
<img src="https://velog.velcdn.com/images/sua_ahn/post/ecec29da-cebb-4a98-aa7d-856f3ae24871/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/03382fd9-d4cd-48bb-8698-2cedb1a18957/image.png" alt="">
→ 완료가 되면 <code>running</code> 상태가 된다.
<img src="https://velog.velcdn.com/images/sua_ahn/post/a54c13e8-c128-4ac4-9307-fd19e5e57b89/image.png" alt=""></p>
<blockquote>
<p>아래와 같이 에러가 뜨면 Vagrantfile 내 synced_folder에 적은 대로 &quot;data&quot; 디렉토리 생성 후 재시도
<img src="https://velog.velcdn.com/images/sua_ahn/post/627f83fb-0cd0-4703-81be-9e54ae19f41b/image.png" alt=""></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/01ff1163-1674-4644-a539-0361e831787d/image.png" alt=""></p>
<p>&nbsp;</p>
<h2 id="4-가상머신-설정">4. 가상머신 설정</h2>
<p>서버 종료 후 설정</p>
<pre><code class="language-shell">&gt;&gt; vagrant halt</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/8b214c66-3785-49af-9e7a-5d81cc8053c8/image.png" alt=""></p>
<p>→ VirtualBox 실행 → 좌측 메뉴에서 서버 선택 → 설정 버튼 
<img src="https://velog.velcdn.com/images/sua_ahn/post/2350e689-f8e2-472a-bbb8-01e80cf4e655/image.png" alt=""></p>
<p>→ 시스템-마더보드에서 기본 메모리를 <strong>2048MB</strong>로 설정
<img src="https://velog.velcdn.com/images/sua_ahn/post/d38ca37f-0289-44f4-bd86-ec5342f8274a/image.png" alt=""></p>
<p>→ 시스템-프로세서 에서 <strong>프로세서 개수를 2개로</strong> 설정
<img src="https://velog.velcdn.com/images/sua_ahn/post/cf00644c-928b-4d37-9732-93060f8b4b35/image.png" alt=""></p>
<p>&nbsp;</p>
<h2 id="5-실행-및-접속">5. 실행 및 접속</h2>
<pre><code class="language-shell">&gt;&gt; vagrant up was01

&gt;&gt; vagrant ssh was01</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/c0017d8b-de74-4b5b-85f8-c5d8aa80f30d/image.png" alt=""></p>
<blockquote>
<p>SSH 세션 종료는 CTRL+D</p>
</blockquote>
<p>&nbsp;</p>
<h3 id="ip-확인">IP 확인</h3>
<pre><code class="language-shell">&gt;&gt; ifconfig</code></pre>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/6eb1b34e-d81f-4757-ab1b-c63678f182bd/image.png" alt=""></p>
<blockquote>
<p><code>-bash: ifconfig: command not found</code>
CentOS7부터는 기본 탑재 명령어가 아님
ifconfig 명령어가 포함된 net-tools package 설치!</p>
</blockquote>
<pre><code class="language-shell">&gt;&gt;&gt; su -
Password: vagrant
&gt;&gt;&gt; yum install net-tools</code></pre>
<p>&nbsp;</p>
<h3 id="sftp-접속">SFTP 접속</h3>
<h4 id="방화벽-설정">방화벽 설정</h4>
<pre><code class="language-shell">방화벽 구동 확인
&gt;&gt; firewall-cmd --state

열린 포트 확인
&gt;&gt; firewall-cmd --zone=public --list-all

포트 개방
&gt;&gt; firewall-cmd --permanent --zone=public --add-port=22/tcp

적용
&gt;&gt; firewall-cmd --reload</code></pre>
<p>&nbsp;</p>
<h4 id="ssh-설정">SSH 설정</h4>
<pre><code class="language-shell">&gt;&gt; vi /etc/ssh/sshd_config</code></pre>
<p>65번째 줄에서 비밀번호 인증 허용</p>
<pre><code class="language-shell">// 기존
PasswordAuthentication no

// 수정
PasswordAuthentication yes</code></pre>
<p>sshd 재시작</p>
<pre><code class="language-shell">&gt;&gt; systemctl restart sshd</code></pre>
<p>&nbsp;</p>
<h4 id="tabby로-sftp-접속">tabby로 SFTP 접속</h4>
<p>좌상단 설정 버튼 - Profiles &amp; connectrions terminal - SSH connection - Duplicate
<img src="https://velog.velcdn.com/images/sua_ahn/post/caa428a3-b1e6-4d9a-b86b-9a11f1e3cae0/image.png" alt=""></p>
<p>Name, Host, Username, Password 입력
(Username, Password 둘 다 <code>vagrant</code>)
<img src="https://velog.velcdn.com/images/sua_ahn/post/6b652a63-4cc6-454e-92b2-4f04593f5900/image.png" alt=""></p>
<p>상단 탭 <code>…</code> 클릭 - Open SFTP panel
<img src="https://velog.velcdn.com/images/sua_ahn/post/b74f9373-a678-47c2-a760-8c2354b23efd/image.png" alt=""></p>
<p>&nbsp;</p>
<hr>
<p>*참고사이트
<a href="https://devopscube.com/vagrant-tutorial-beginners/">https://devopscube.com/vagrant-tutorial-beginners/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 전략 패턴 Strategy Pattern in Spring(Java)]]></title>
            <link>https://velog.io/@sua_ahn/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4-Strategy-Pattern</link>
            <guid>https://velog.io/@sua_ahn/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4-Strategy-Pattern</guid>
            <pubDate>Wed, 03 Apr 2024 07:49:56 GMT</pubDate>
            <description><![CDATA[<p>프로그래밍을 축구로 비유하면, 알고리즘은 축구 기술, 디자인 패턴은 축구 <strong>전술</strong>과 유사하다. 경기를 승리로 이끌기 위해 전술을 배워보자!</p>
<h1 id="전략-패턴">전략 패턴</h1>
<p>: 객체의 행위를 동적으로 바꾸고 싶은 경우, 직접 행위를 수정하지 않고 <strong>전략을 바꿔 주기만 함으로써</strong> 행위를 유연하게 확장하는 방법</p>
<ul>
<li><p>디자인 패턴의 목적에 따라 3가지로 분류 가능
→ 행동 패턴에 속함</p>
<blockquote>
<p>행동 패턴 
: 클래스와 객체들이 <strong>상호작용</strong>하고 <strong>역할</strong>을 분담하는 방법</p>
</blockquote>
</li>
<li><p>참고사이트의 예시로, <strong>내비게이션 기능</strong>을 설명하였다. </p>
<ul>
<li>차, 도보, 자전거 등 이동수단에 따라 <strong>경로탐색 알고리즘</strong>이 달라지고, 알고리즘의 수정이 빈번할뿐만 아니라 탐색 조건에 따라 새로운 경로탐색 알고리즘이 추가될 수 있다. </li>
<li>따라서, 내비게이션 기능에 차, 도보 등 각각의 경로탐색 알고리즘을 직접 사용하지 않고, <strong>동일한 경로탐색 인터페이스</strong>로 묶어서 참조한다. 내비게이션 기능은 사용자가 선택하는 경로탐색 알고리즘에 따라서 실행될 수 있다.</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="패턴-다이어그램">패턴 다이어그램</h2>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/6acfd044-badc-4aa0-8caa-784a2254c23e/image.png" alt=""></p>
<p><strong>Strategy Interface</strong>와 그 구현체인 <strong>Strategy</strong>, 인터페이스를 통해 구현체를 실행하는 <strong>Context</strong>로 구성된다.</p>
<p>&nbsp;</p>
<hr>
<h2 id="springjava-예시">Spring(Java) 예시</h2>
<p>JDBC 연결을 예로 들어보자.</p>
<h3 id="strategy-interface">Strategy interface</h3>
<p>Context가 전략을 실행하는 데 사용하는 <strong>메서드를 선언</strong>
(해당 예시는 함수형 인터페이스 사용)</p>
<pre><code class="language-java">@FunctionalInterface
public interface ConnectionCreator {

    Connection createConnection();
}</code></pre>
<p>&nbsp;</p>
<h3 id="context">Context</h3>
<p>전략 인터페이스를 통해서만 전략 객체와 통신
→ context가 strategy에 의존하지 않음 
⇒ 새 알고리즘(전략) 수정, 추가 용이</p>
<pre><code class="language-java">public class JDBCRepository {

    private ConnectionCreator creator;

    public JDBCRepository(ConnectionCreator creator) {
        this.creator = creator;
    }

    public Map&lt;String, Object&gt; selectOne(String sql, List&lt;String&gt; columns){

        Map&lt;String, Object&gt; res = new LinkedHashMap&lt;&gt;();

        Connection conn = null;
        PreparedStatement pstm = null;
        ResultSet rset = null;

        try {
            conn = creator.createConnection();    // 호출; 어떤 알고리즘이 적용되는지 신경 쓰지 않아도 됨
            pstm = conn.prepareStatement(sql);
            rset = pstm.executeQuery();

            while(rset.next()) {
                for (String col : columns) {
                    res.put(col, rset.getObject(col));
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return res;
    }
}</code></pre>
<p>&nbsp;</p>
<h3 id="client">Client</h3>
<ul>
<li><p>특정 전략 객체를 만들어 Context에 전달
(해당 예시는 람다식으로 익명 클래스와 익명 함수를 구현해서 전달)</p>
</li>
<li><p>런타임 중 알고리즘(전략) 변경 가능</p>
<pre><code class="language-java">public class App {

  public void start() {

      JDBCRepository jdbcRepository = new JDBCRepository(() -&gt; {

          String url = &quot;jdbc:h2:./test;MODE=MySQL;DATABASE_TO_LOWER=TRUE&quot;;
          Connection conn = null;

          try {
              conn = DriverManager.getConnection(url, &quot;sa&quot;, &quot;sa&quot;);
              conn.setAutoCommit(false);
          } catch (SQLException e) {
              e.printStackTrace();
          }
          return conn;
      });

      System.out.println(
          jdbcRepository.selectOne(
              &quot;select * from member&quot;
              , List.of(&quot;user_id&quot;,&quot;password&quot;)));
  }
}</code></pre>
</li>
</ul>
<p>&nbsp;</p>
<hr>
<p>*참고 사이트
<a href="https://refactoring.guru/ko/design-patterns/strategy">https://refactoring.guru/ko/design-patterns/strategy</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MySQL, MariaDB] auto_increment VS max(id)+1]]></title>
            <link>https://velog.io/@sua_ahn/MySQL-MariaDB-autoincrement-VS-maxid</link>
            <guid>https://velog.io/@sua_ahn/MySQL-MariaDB-autoincrement-VS-maxid</guid>
            <pubDate>Wed, 27 Mar 2024 08:42:05 GMT</pubDate>
            <description><![CDATA[<p>운영중인 솔루션 추가 개발 중, 
MySQL, MariaDB 시퀀스 컬럼에 <strong>auto_increment</strong> 설정이 되어있음에도 
SELECT문으로 <strong>최댓값을 조회</strong>하여 INSERT하는 부분 발견!</p>
<pre><code class="language-sql">SELECT MAX(ID) + 1 FROM table;

-- 위에서 조회한 값을 아래 쿼리에서 사용
INSERT INTO table (ID, other columns...)
VALUES (100, ...);</code></pre>
<p>초기 개발 시나 테스트 때 데이터가 안정되지 못하여 사용하던 것이 운영에도 남아있던 것이었다.</p>
<p>auto_increment를 사용하도록 수정하기 전에 다음 사항들을 확인하였다.</p>
<ol>
<li><p>조회한 값을 INSERT할 때 외에 <strong>따로 사용하는지</strong> 확인했으나, 따로 사용하지 않음 
(솔루션 특성상 해당 코드를 ORACLE 등 다른 DB에도 사용하여 MyBatis의 &#39;useGeneratedKey&#39;기능을 사용하긴 어려움)</p>
</li>
<li><p>해당 솔루션은 <strong>분산DB(다중DB)</strong>에는 사용하지 않으므로 auto_increment 사용 가능</p>
</li>
<li><p>Oracle에서는 NEXTVAL을 사용하므로, <strong>삭제로 인한 id값 상승</strong>을 막기 위함 아님</p>
</li>
<li><p>EXPLAIN문으로 실행계획을 조회해본 결과, 인덱스 1건만 읽어 성능에 큰 문제를 일으키는 것은 아님</p>
<pre><code class="language-sql">EXPLAIN
SELECT MAX(ID) + 1 FROM table;</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/90843bf7-c291-4158-b0db-c80a2000e349/image.png" alt=""></p>
<blockquote>
<p>Select tables optimized away
Min() 또는 Max()만 SELECT절에 사용되거나, GROUP BY로 MIN(), MAX()를 조회하는 쿼리가 <strong>인덱스를 오름차순 또는 내림차순으로 1건만 읽는 최적화</strong>가 적용된다면 표시된다.
(특정 인덱스를 1건만 읽으면 되는 경우)</p>
</blockquote>
<p>The optimizer determined 1) that at most one row should be returned, and 2) that to produce this row, a deterministic set of rows must be read. When the rows to be read can be read during the optimization phase (for example, by reading index rows), there is no need to read any tables during query execution.</p>
<blockquote>
</blockquote>
<p>The first condition is fulfilled when the query is implicitly grouped (contains an aggregate function but no GROUP BY clause). The second condition is fulfilled when one row lookup is performed per index used. The number of indexes read determines the number of rows to read.</p>
<hr>
<p>*참고사이트
<a href="https://neunggu.tistory.com/42">https://neunggu.tistory.com/42</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[멀티스레드 프로그래밍 v1.헷갈리는 이론들을 정리해보자]]></title>
            <link>https://velog.io/@sua_ahn/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D1</link>
            <guid>https://velog.io/@sua_ahn/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D1</guid>
            <pubDate>Tue, 27 Feb 2024 04:41:42 GMT</pubDate>
            <description><![CDATA[<h1 id="멀티스레드를-통한-병렬-프로그래밍">멀티스레드를 통한 병렬 프로그래밍</h1>
<h2 id="동시성-vs-병렬성">동시성 vs 병렬성</h2>
<p>Concurrency vs Parallelism</p>
<p>일반적으로 동시성 ⊃ 병렬성</p>
<ul>
<li><p>동시 실행 
: CPU가 하나만 있는 경우, 응용 프로그램은 정확히 동시에 두 개 이상의 작업을 진행하지 못할 수 있다. 둘 이상의 작업을 동시에 진행하기 위해 CPU는 서로 다른 작업 간에 전환할 수 있다.</p>
</li>
<li><p>병렬 실행 
: 컴퓨터에 둘 이상의 CPU 또는 CPU 코어가 있어서 둘 이상의 작업을 동시에 병렬적으로 진행할 수 있다.</p>
</li>
</ul>
<blockquote>
<p>동시와 병렬 뒤에 <em>성질, 실행, 프로그래밍</em> 중 어떤 것이 붙느냐에 따라 의미가 달라지고, 용어의 구별에 대해 _완전한 합의_가 이루어지지 않았으므로 대략적으로만 알고 있으면 될 듯 하다.
<img src="https://velog.velcdn.com/images/sua_ahn/post/c8e47c76-7e32-4e7d-aaf7-b4517e5c8bd7/image.png" alt=""></p>
</blockquote>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr>
<h1 id="멀티스레드에서-주의해야-하는-것">멀티스레드에서 주의해야 하는 것</h1>
<h2 id="👓-메모리-가시성-visibility">👓 메모리 가시성 (Visibility)</h2>
<p>여러 스레드가 각각의 CPU에서 실행되는 경우를 생각해보자!
각각의 CPU는 Cache를 가지고 있어서, 서로 다른 스레드는 Cache에서 <strong>업데이트되지 않은 이전 데이터(Stale Data)</strong>를 읽을 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/97973a0f-a9b9-4b5a-9b34-27ab76f8e53f/image.png" alt=""></p>
<p>이때, <strong>변수(객체)에 가시성을 부여</strong>하여, 반드시 Main Memory에 값을 쓰거나 읽도록 할 수 있다.</p>
<blockquote>
<p><strong>CPU Cache</strong>
Memory Wall 문제를 개선하기 위해 CPU 칩 안에 위치한 메모리</p>
</blockquote>
<p><strong>Memory Wall</strong>
CPU 발전속도보다 Memory 발전속도가 느리기 때문에
컴퓨터 시스템의 전체적인 성능이 Memory에 의해 결정되는 현상
→ 느린 Memory 접근으로 인해 지연과 에너지 소모 등의 문제가 발생</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2 id="🤝-공유자원">🤝 공유자원</h2>
<p>자바 멀티스레드 환경에서는 스레드들끼리 static영역과 heap영역을 공유한다. 따라서 <strong>공유자원에 대한 동기화 문제</strong>를 신경써야 한다.
<img src="https://velog.velcdn.com/images/sua_ahn/post/b6bb2b9c-e4ae-4a4c-9dd1-34325e368ac9/image.png" alt=""></p>
<h3 id="데이터-레이스-data-race">데이터 레이스 (Data Race)</h3>
<p>여러 쓰레드가 <strong>공유자원에 동시에 접근하는 것</strong>을 데이터 레이스(Data Race, 데이터 경쟁) 라고 한다. 공유자원을 동시에 읽는 것 자체는 문제가 되지 않는다. 
그러나, 서로 다른 두 스레드가 같은 공유 변수에 동시에 값을 할당하거나, 할당 중에 다른 스레드가 읽는 경우 잘못된 값을 읽고 쓸 수 있다. 이러한 상황에서는 원자적 연산을 사용하여 다른 연산이 중간에 끼어들지 못하게 할 수 있다.</p>
<blockquote>
<p><strong>경쟁 조건(race condition)</strong>
 두 개 이상의 프로세스가 공통 자원을 병행적으로 읽거나 쓸 때, 공유 데이터에 대한 접근이 <strong>어떤 순서</strong>에 따라 이루어졌는지에 따라 그 <strong>실행 결과</strong>가 달라지는 상황</p>
</blockquote>
<p>&nbsp;</p>
<h3 id="연산의-원자성-확보-단일-연산">연산의 원자성 확보 (단일 연산)</h3>
<p>공유변수를 사용할 때 문제가 되는 경우는 <strong>여러 스레드가 단일 연산이 아닌 연산을 동시에 수행하는 경우</strong>이다.</p>
<p>이때, <strong>단일 연산</strong>은 하나의 일관된 동작이 한 Thread에서만 시작되고 종료되는 것을 의미하며, 따라서 다른 Thread에 의해 간섭 받을 수 없다.</p>
<p>&#39;단일 연산&#39;이란 개념을 처음 접하면 코드 한 줄은 단일 연산일 것이라고 쉽게 생각할 수 있다. 그러나 한 줄의 statement가 컴파일러에 의해 기계어로 변경되는 과정에서, 여러 개의 machine instruction이 만들어져 실행되면 다른 Thread에 의해 간섭 받을 수 있다. </p>
<p>그 대표적인 예로 <code>i++</code> 연산을 들 수 있다. 연산 과정은 다음과 같다.</p>
<ol>
<li><code>i</code>의 기존 값을 읽는다.  (READ : Memory → Cache)</li>
<li><code>i</code>에 1을 더한다.  (MODIFY : in Cache)</li>
<li><code>i</code>의 값을 변수에 할당한다.  (WRITE : Cache → Memory)</li>
</ol>
<p>3가지 동작 수행 중 다른 스레드에 의해 덮어쓰여질 수 있다.</p>
<p>&nbsp;</p>
<h3 id="연산-재배열-reordering">연산 재배열 (Reordering)</h3>
<p>메모리는 캐시에 비해 매우 느리기 때문에, 메인 메모리에 접근하는 횟수를 최소화하고 캐시를 적극 활용해야 한다. <strong>캐시를 효과적으로 사용하기 위해 컴파일러와 CPU는 참조 지역성의 원리*에 의해 메모리 연산을 재배열한다.</strong></p>
<p>그런데 이로 인해 서로 다른 스레드들의 작업 순서를 예측할 수 없게 되고, 타이밍에 따라 결과가 달라질 수 있다. 예를 들어, 공유 변수를 다른 스레드의 작업을 시작 또는 중단시키는 flag로 사용한다면, 이 변수에 대한 접근이 어떤 순서로 이루어졌느냐에 따라 매번 다른 결과를 만들어낼 수 있다.</p>
<blockquote>
<p><strong>참조 지역성의 원리</strong>
CPU가 사용할 데이터를 예측하는 방법</p>
</blockquote>
<ul>
<li>시간 지역성
: 최근 접근했던 메모리에 다시 접근하는 경향이 있음</li>
<li>공간 지역성
: 접근한 메모리 공간 근처를 접근하려는 경향이 있음</li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr>
<h1 id="멀티스레드의-필수-조건">멀티스레드의 필수 조건</h1>
<ol>
<li><p>안정성 (데이터의 일관성)
↔ 프로그램이 의도대로 실행되지 않는 상황</p>
</li>
<li><p>활동성 (생존성)
↔ 다른 스레드의 영향으로 스레드가 멈추거나 대기하는 상황</p>
</li>
</ol>
<p>&nbsp;</p>
<h2 id="🙆♀️-thread-safe-스레드-안정성">🙆‍♀️ Thread-safe (스레드 안정성)</h2>
<p>멀티스레드 프로그래밍에서 어떤 함수나 변수, 혹은 객체가 여러
스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에는 문제가 없음을 말한다. 이는 곧 데이터의 무결성을 의미한다.</p>
<p>그렇다면 스레드 안정성을 지키기 위해 가장 중요한 것은 무엇일까?</p>
<p>객체(변수)는 객체지향 프로그래밍의 가장 기초가 되므로, 이에 대한 접근을 관리하는 것이 중요하다. 특히, <strong>공유되고 변경가능한 상태에 대한 접근을 관리</strong>하는 것이 가장 중요하다.</p>
<p>공유 됐다는 것은 여러 스레드가 특정 변수에 접근할 수 있다는 뜻이고, 변경할 수 있다(mutable)는 것은 해당 변수 값이 여러 스레드에 의해 변경될 수 있다는 뜻이다.</p>
<p>따라서, 스레드에 안전한 코드를 작성하기 위해서는 아래 세가지 중 하나는 반드시 지켜야 한다.</p>
<ol>
<li><p>상태 변수를 스레드 간에 공유하지 않음 
ex. 로컬 변수를 메서드 내에서만 사용하고 반환하지 않음</p>
</li>
<li><p>상태 변수를 변경할 수 없도록 만듦
ex. <code>final</code> 불변 객체</p>
</li>
<li><p>상태 변수에 접근할 땐 언제나 동기화를 사용 
ex. <code>syncronized</code></p>
</li>
</ol>
<blockquote>
<p>프로그램이 정확하게 동작함 = 클래스 명세에 부합함</p>
</blockquote>
<p>&nbsp;</p>
<h3 id="스레드의-재진입성-reentrant">스레드의 재진입성 (Reentrant)</h3>
<p>재진입성이란, 특정 스레드가 자기가 이미 획득한 락을 다시 확보할 수 있는 특성을 말한다. 위 설명만 들으면 &#39;그래서 뭐 어떻다는 건데?&#39;싶다. 일반적으로 재진입성과 스레드 안정성을 많이 혼동한다. 둘 다 자원을 처리하는 방법과 관련되나, 다른 개념이다.</p>
<p>재진입 가능한 함수는 <strong>하나의 스레드</strong>에서 여러 번 호출되어도 안전하게 동작할 수 있다. 이러한 특성은 <strong>여러 스레드</strong>에서 동시에 호출되어도 안전하게 동작할 수 있음을 의미한다. 따라서 재진입 가능한 함수는 스레드에 안전한 함수를 <strong>포함</strong>하는 개념이 된다.</p>
<h4 id="재진입-가능한-함수의-일반적인-특징">재진입 가능한 함수의 일반적인 특징</h4>
<ul>
<li><p>자기 동기화(Self-Synchronizing)
: 함수 내부에서 사용되는 모든 자원에 대한 재진입이 가능하다. 즉, 함수 내부에서 락 또는 다른 동기화 메커니즘을 사용하여 자원을 보호하지 않아도 된다. (<strong>동기화 연산 필요 없음</strong>)</p>
</li>
<li><p>상태 저장(State Preservation)
: 함수가 호출되는 동안의 상태를 안전하게 저장하고 관리한다. 이는 재진입 가능한 함수가 중복 호출되었을 때 각 호출이 <strong>독립적</strong>으로 자신의 상태를 유지할 수 있도록 한다.</p>
</li>
</ul>
<h4 id="재진입-가능한-함수의-대표적인-예시">재진입 가능한 함수의 대표적인 예시</h4>
<ul>
<li><p>재귀 함수
: 함수가 자기 자신을 호출하는 재귀 호출을 사용하는 경우</p>
</li>
<li><p>ReentrantLock 클래스
: Java에서 제공하는 재진입 가능한 락(lock) 구현체</p>
</li>
</ul>
<h4 id="jvm에서-재진입은-어떻게-동작하는가">JVM에서 재진입은 어떻게 동작하는가?</h4>
<ol>
<li><p>스레드가 락을 확보함</p>
</li>
<li><p>JVM은 락을 소유한 스레드를 기록하고 <strong>확보 횟수(lock count)</strong>를 1로 지정함</p>
</li>
<li><p>같은 스레드가 락을 다시 얻으면 횟수를 증가시킴</p>
</li>
<li><p>이 스레드가 synchronized 블록 밖으로 나가 락이 회수되면 횟수가 감소함</p>
</li>
<li><p>횟수가 0이 되면 해당 락이 해제됨</p>
</li>
</ol>
<p>&nbsp;</p>
<h2 id="🙆♂️-활동성">🙆‍♂️ 활동성</h2>
<p>재진입성은 재귀호출을 포함한 병렬실행을 보장하고 스레드 간에 <strong>동기화 연산을 필요로 하지 않는다.</strong> 반면, thread-safe하다는 것은 단지 여러 스레드에 의해 실행되더라도 문제만 없으면 된다는 완화된 조건이므로, 여러 스레드가 공유 자원에 동시에 접근하지 못하도록 lock으로 막아주기만 하면 된다. </p>
<p>결국, thread-safe한 코드는 멀티스레드 환경에서 reentrant 코드보다는 효율성이 떨어질 가능성이 높다. 다른 스레드에 의해 공유자원에 lock이 걸려 있다면, lock이 해제되기를 기다리며 수행을 멈추기 때문이다.
또한, 과도한 동기화로 인해 성능이나 활동성에 문제가 생길 수 있다.</p>
<h3 id="교착-상태-deadlock">교착 상태 (Deadlock)</h3>
<p>: 둘 이상의 스레드가 <strong>서로</strong>가 가진 리소스를 기다리며 <strong>무한정</strong> 대기 상태에 빠지는 현상을 말한다.</p>
<h4 id="데드락-방지-방법">데드락 방지 방법</h4>
<ul>
<li><p>적절한 동기화 : 상호 배제를 사용한 동기화 피하기</p>
</li>
<li><p>락 순서 관리 : 락을 일관된 순서로 획득하도록 함</p>
</li>
<li><p>타임아웃 설정 : 락을 획득하지 못하고 일정 시간이 경과하면, 해당 락을 다시 요청하거나 다른 처리 방법을 선택하도록 함</p>
</li>
<li><p>동기화 방식 변경 : ReentrantLock 등의 락클래스 사용</p>
</li>
<li><p>데드락 탐지 및 해결 : 프로그램이 실행되는 동안 데드락 발생을 주기적으로 검사하고, 데드락이 발생할 경우 이를 해결하는 알고리즘을 구현</p>
</li>
</ul>
<blockquote>
<p><strong>스레드 블로킹 (Thread Blocking)</strong>
: 스레드가 어떠한 이유로 <strong>일시적으로</strong> 작업을 중단하고 대기 상태에 들어가는 현상을 말한다.
예를 들어, 스레드가 파일을 읽거나 네트워크 연결을 기다리는 동안 블로킹 상태에 들어갈 수 있다.
한 스레드가 블로킹 상태에 들어가면 다른 스레드도 영향을 받아 일시적으로 멈출 수 있다</p>
</blockquote>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr>
<h4 id="참고사이트-및-책">참고사이트 및 책</h4>
<ul>
<li><p>동시성과 병렬성
<a href="https://jenkov.com/tutorials/java-concurrency/index.html">https://jenkov.com/tutorials/java-concurrency/index.html</a>
<a href="https://stackoverflow.com/questions/1897993/what-is-the-difference-between-concurrent-programming-and-parallel-programming/57223044#57223044">https://stackoverflow.com/questions/1897993/what-is-the-difference-between-concurrent-programming-and-parallel-programming/57223044#57223044</a></p>
</li>
<li><p>가시성
<a href="https://rightnowdo.tistory.com/entry/JAVA-concurrent-programming-Visibility%EA%B0%80%EC%8B%9C%EC%84%B1">https://rightnowdo.tistory.com/entry/JAVA-concurrent-programming-Visibility%EA%B0%80%EC%8B%9C%EC%84%B1</a></p>
</li>
<li><p>연산 재배열
<a href="https://medium.com/spoontech/%EC%9B%90%EC%9E%90%EC%A0%81-atomic-%EC%97%B0%EC%82%B0%EA%B3%BC-%EC%88%9C%EC%84%9C-ordering-%EC%A0%9C%EC%95%BD-ed25e39c4646">https://medium.com/spoontech/%EC%9B%90%EC%9E%90%EC%A0%81-atomic-%EC%97%B0%EC%82%B0%EA%B3%BC-%EC%88%9C%EC%84%9C-ordering-%EC%A0%9C%EC%95%BD-ed25e39c4646</a></p>
</li>
<li><p>스레드 안정성
<a href="https://clairdelunes.tistory.com/47">https://clairdelunes.tistory.com/47</a>
<a href="https://kimfk567.tistory.com/118?category=1057557">https://kimfk567.tistory.com/118?category=1057557</a>
<a href="https://jeong-pro.tistory.com/156">https://jeong-pro.tistory.com/156</a>
<a href="https://yangbongsoo.gitbook.io/study/singleton">https://yangbongsoo.gitbook.io/study/singleton</a>
<a href="https://jeong-pro.tistory.com/156">https://jeong-pro.tistory.com/156</a>
멀티코어를 100%로 활용하는 자바 병렬 프로그래밍</p>
</li>
<li><p>재진입성
<a href="https://ohtaeg.tistory.com/13">https://ohtaeg.tistory.com/13</a>
<a href="https://yesarang.tistory.com/214">https://yesarang.tistory.com/214</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[programmers 탐욕법 Greedy (Java)]]></title>
            <link>https://velog.io/@sua_ahn/programmers-%ED%83%90%EC%9A%95%EB%B2%95-Greedy-Java</link>
            <guid>https://velog.io/@sua_ahn/programmers-%ED%83%90%EC%9A%95%EB%B2%95-Greedy-Java</guid>
            <pubDate>Wed, 27 Dec 2023 07:20:07 GMT</pubDate>
            <description><![CDATA[<h1 id="체육복">체육복</h1>
<p>Level 1</p>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        int answer = n - lost.length;
        Arrays.sort(lost);
        List&lt;Integer&gt; rList = Arrays.stream(reserve)                        
                                    .boxed()
                                    .collect(Collectors.toList());
        for(int i = 0; i &lt; lost.length; i++) { 
            int rInd = rList.indexOf(lost[i]);
            if(rInd != -1) {    // 잃어버렸지만 여분이 있는 경우
                answer++;
                lost[i] = 0;    // 잃어버리지 않음으로 표시
                rList.set(rInd, -1);    // 여분없음으로 표시
            }
        }

        for(int l : lost) {
            if(l == 0) continue;    // 위에서 처리됨
            // 왼쪽에 여분이 있는지 확인
            int rInd = l &gt; 1 ? rList.indexOf(l-1) : -1;
            // 오른쪽에 여분이 있는지 확인
            rInd = (rInd == -1 &amp;&amp; l &lt; n) ? rList.indexOf(l+1) : rInd;

            if(rInd != -1) {    // 빌릴 수 있는 경우
                answer++;
                rList.set(rInd, -1);
            }
        }

        return answer;
    }
}</code></pre>
<h1 id="조이스틱">조이스틱</h1>
<p>Level 2</p>
<pre><code class="language-java">class Solution {
    public int solution(String name) {
        char[] arr = name.toCharArray();
        int change = 0;             // 알파벳 변경 최소 횟수
        int move = arr.length - 1;  // 이동 최댓값으로 초기화

        for(int i = 0; i &lt; arr.length; i++) {
            change += Math.min(arr[i] - 65, 91 - arr[i]);   // 대문자 65~90

            int index = i + 1;    // A가 연속으로 나오지 않기 시작하는 인덱스 찾기
            while(index &lt; arr.length &amp;&amp; arr[index] == &#39;A&#39;) index++;

            move = Math.min(move, Math.min(i * 2 + (arr.length - index), 
                                           i + (arr.length - index) * 2));
        }
        return change + move;
    }
}</code></pre>
<h1 id="큰-수-만들기">큰 수 만들기</h1>
<p>Level 2</p>
<pre><code class="language-java">class Solution {
    // 스택
     public String solution1(String number, int k) {
        Stack&lt;Character&gt; stack = new Stack&lt;&gt;();    // 남길 수 저장

        for (int i = 0; i &lt; number.length(); i++) {
            char c = number.charAt(i);    // 비교 대상(앞자리 수)

            while (!stack.isEmpty() &amp;&amp; stack.peek() &lt; c &amp;&amp; k-- &gt; 0) {
                // 비교 대상보다 작고, 아직 제거해야 될 경우, 스택에서 삭제
                stack.pop();    
            }
            stack.push(c);
        }

        char[] result = new char[number.length() - k];
        for (int i=0; i&lt;result.length; i++) {
            result[i] = stack.get(i);
        }
        return new String(result);
    }

    // 투 포인터
    public String solution2(String number, int k) {
        int numLen = number.length();
        StringBuilder sb = new StringBuilder(number);
        int p = 1;    // 포인터
        boolean flag = false;

        while(sb.length() + k &gt; numLen) {
            char a = sb.charAt(p - 1);
            char b = sb.charAt(p);

            if(a &gt;= b) {    // 앞자리 수가 더 큰 경우
                if(sb.length() == p + 1) {    // 포인터가 맨끝으로 간 경우
                    sb.deleteCharAt(p);    // 작은 수 삭제
                } else {    // 삭제 보류
                    p++;    // 뒷자리 수들 비교하도록 포인터 이동
                    flag = true;
                    continue;
                }
            } else {     // 뒷자리 수가 더 큰 경우
                sb.deleteCharAt(p-1);    // 앞에 작은 수 삭제
                if(flag) p = 1;        // 초기화 -&gt; 맨앞부터 다시 비교
            }

            if(sb.length() == p) p = 1;    // 포인터가 더이상 뒤로 이동할 수 없는 경우
            flag = false;
        }

        return sb.toString();
    }
}</code></pre>
<h1 id="구명보트">구명보트</h1>
<p>Level 2</p>
<ul>
<li>투 포인터<pre><code class="language-java">import java.util.*;
</code></pre>
</li>
</ul>
<p>class Solution {
    public int solution(int[] people, int limit) {
        Arrays.sort(people);    // 가벼운 사람부터 정렬
        int p1 = 0;  // 남아있는 가장 가벼운 사람 인덱스, 2명이 타는 경우의 수
        int p2 = people.length - 1;    // 남아있는 가장 무거운 사람 인덱스</p>
<pre><code>    while(p1 &lt; p2) {
        if(people[p1] + people[p2] &lt;= limit) p1++;
        p2--;
    }
    return people.length - p1;
}</code></pre><p>}</p>
<pre><code>
# 섬 연결하기
Level 3
* 크루스칼 알고리즘 (최소 신장 트리)
```java
import java.util.*;

class Solution {
    public int solution(int n, int[][] costs) {
        int answer = 0;
        List&lt;int[]&gt; sortedCosts = new ArrayList&lt;&gt;();
        Map&lt;Integer, Integer&gt; connected = new HashMap&lt;Integer, Integer&gt;();    // 연결 선 저장 

        for(int[] c : costs) {
            sortedCosts.add(c);
            // 아직 연결된 선이 없으므로, 본인을 가리키도록 설정
            connected.putIfAbsent(c[0], c[0]);
            connected.putIfAbsent(c[1], c[1]);
        }
        Collections.sort(sortedCosts, (a,b) -&gt; a[2] - b[2]);    // 적은 비용부터 정렬

        for(int i = 0; i &lt; costs.length; i++) {
            int edge1 = findEdge(connected, sortedCosts.get(i)[0]);
            int edge2 = findEdge(connected, sortedCosts.get(i)[1]);
            // 순환되지 않으면, 섬 연결
            if(edge1 != edge2) {
                if(edge1 &lt; edge2) {    // key 큰 수 -&gt; value 작은 수
                    connected.replace(edge2, edge1);
                } else {
                    connected.replace(edge1, edge2);
                }
                answer += sortedCosts.get(i)[2];    // 비용 추가
            }
        }

        return answer;
    }

    private int findEdge(Map&lt;Integer, Integer&gt; map, int start) {
        // base case
        if(map.get(start) == start) return start;
        // recursive case
        map.replace(start, findEdge(map, map.get(start)));
        return map.get(start);
    }
}</code></pre><h1 id="단속카메라">단속카메라</h1>
<p>Level 3</p>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int[][] routes) {
        int answer = 0;
        List&lt;int[]&gt; sortedRoutes = new ArrayList&lt;&gt;();

        for(int[] route : routes) {
            sortedRoutes.add(route);
        }
        Collections.sort(sortedRoutes, (a,b) -&gt; a[1] - b[1]);   // 빨리 나가는 순서대로 정렬

        while(!sortedRoutes.isEmpty()) {
            // 남은 경로 중 먼저 나가는 지점에 카메라 설치
            int camera = sortedRoutes.get(0)[1];

            for(int i = 0; i &lt; sortedRoutes.size(); i++) {
                // 위에서 설치한 카메라를 지나가는 경우
                if(sortedRoutes.get(i)[0] &lt;= camera 
                    &amp;&amp; sortedRoutes.get(i)[1] &gt;= camera) {

                    sortedRoutes.remove(i);
                    i--;
                }
            }
            answer++;
        }
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 REST API (Polling 모델, Callback 모델)]]></title>
            <link>https://velog.io/@sua_ahn/%EB%B9%84%EB%8F%99%EA%B8%B0-REST-API</link>
            <guid>https://velog.io/@sua_ahn/%EB%B9%84%EB%8F%99%EA%B8%B0-REST-API</guid>
            <pubDate>Wed, 13 Dec 2023 06:34:24 GMT</pubDate>
            <description><![CDATA[<h1 id="rest-api-통신">REST API 통신</h1>
<p>REST 아키텍처 패턴에서는 일반적으로 동기 통신을 사용한다.
그 말인 즉, REST API의 클라이언트는 HTTP 엔드포인트를 호출할 때마다 즉각적인(약 60초 내) 응답을 기대하고, 제한 시간 내에 응답을 받지 못하면 시간 초과 메시지와 에러를 던진다.
<img src="https://velog.velcdn.com/images/sua_ahn/post/35de16ba-cd4b-4bb3-ad9e-474f677d319f/image.png" alt=""></p>
<p>그렇다면 제출된 요청을 처리하는 데 오랜 시간이 걸릴 것으로 예상되면 어떻게 해야 될까?</p>
<p>클라이언트가 응답을 기다리지 않고, 요청이 처리된 후 결과를 확인할 수 있도록 비동기 통신을 고려해 볼 수 있다.</p>
<p>비동기 통신을 사용하면 클라이언트는 응답을 기다리는 시간을 효율적으로 사용할 수 있다. 또한, 동기 통신이었다면 응답을 기다렸을 스레드가 block 상태가 되지 않고, 다른 작업에 사용 가능하다. 서버도 마찬가지이다. 외부 API 호출을 기다리는 제약을 제거할 수 있다.</p>
<p>오랜 시간이 걸리는 복잡한 작업은 간단한 작업보다 더 많은 오류를 발생시키는 경향이 있다. 비동기 통신의 경우, 트랜잭션에서 문제가 발생해도 서버는 전체 프로세스를 다시 시작하고 사용자에게 결과를 알릴 수 있다.</p>
<p>더 자세한 사항은 대표적인 비동기 통신 모델을 살펴보며 알아보자.</p>
<h1 id="비동기-통신">비동기 통신</h1>
<h2 id="polling-model">Polling Model</h2>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/e6555551-b1bc-475d-94f2-64a4fc448caa/image.png" alt=""></p>
<blockquote>
<p>*<em>Polling *</em>
다른 프로그램의 상태를 주기적으로 검사하여 일정 조건을 만족할 때 데이터를 처리하는 방식</p>
</blockquote>
<p>클라이언트는 서비스에 요청을 보내 작업을 제출하고, 제출한 작업과 관련된 토큰을 즉시 응답받는다. 클라이언트는 제출한 작업의 상태를 추적하기 위해 이 토큰을 사용하여 서비스를 폴링해야 한다. </p>
<p>만약 서버의 응답이 지연될 경우, 클라이언트 스레드는 <strong>여러번 응답을 확인</strong>해야 하므로 오버헤드가 발생할 수 있다. 또한, 클라이언트는 작업 상태를 추적하고 응답을 가져오는 <strong>책임</strong>을 갖게 된다.</p>
<p>그러나 <strong>동기적</strong>으로(클라이언트가 원하는 시점에) 서버의 응답을 확인할 수 있다는 장점이 있다. 또한, 클라이언트 측에서 중단이 발생하는 경우 클라이언트가 제출한 작업에 대한 폴링을 재개할 수 있으므로 제출된 <strong>작업의 상태/출력이 손실되지 않는다.</strong> 특히, 클라이언트 측 <strong>인바운드 트래픽이 허용되지 않는 네트워크</strong> 설정(조직 방화벽)에 있는 경우에 유리하다. </p>
<h2 id="callback-model">Callback Model</h2>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/11ec1113-c140-4bda-b26d-8928d66556be/image.png" alt=""></p>
<p>클라이언트(Caller)는 요청을 보낸 후 응답이 오는 것을 신경쓰지 않는다. 서버(Callee)에서 작업이 완료된 후에는 지정된 콜백(Callback) URI를 통해 응답을 전달받는다.</p>
<p>이러한 콜백 모델에서는 통제권의 역전이 일어난다. 작업이 완료되면 클라이언트에게 작업 결과를 알리는 것이 <strong>서비스의 책임</strong>이 된다. 또한, <strong>클라이언트가 서비스 end-point을 노출</strong>하고, 서비스 end-point을 콜백 URI로 서비스에서 사용하게 된다. 따라서, 클라이언트는 더 이상 단순한 클라이언트가 아니며 이제 서버이기도 하다.</p>
<p>특히 서버의 응답이 <strong>비동기적</strong>이어도 문제가 되지않을 때 사용한다. 서버의 응답을 여러번 확인하지 않아도 된다는 장점이 있으나, 콜백이 <strong>누락</strong>될 수 있다.</p>
<p>&nbsp;</p>
<hr>
<p>참고사이트
<a href="https://sanketdaru.com/blog/polling-model-async-rest-spring-boot/">https://sanketdaru.com/blog/polling-model-async-rest-spring-boot/</a>
<a href="https://stackoverflow.com/questions/23580494/why-callback-in-rest-api">https://stackoverflow.com/questions/23580494/why-callback-in-rest-api</a>
<a href="https://qkfmxha.tistory.com/55">https://qkfmxha.tistory.com/55</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프록시, VPN, 방화벽, IPS]]></title>
            <link>https://velog.io/@sua_ahn/%ED%94%84%EB%A1%9D%EC%8B%9C-VPN-%EB%B0%A9%ED%99%94%EB%B2%BD-IPS</link>
            <guid>https://velog.io/@sua_ahn/%ED%94%84%EB%A1%9D%EC%8B%9C-VPN-%EB%B0%A9%ED%99%94%EB%B2%BD-IPS</guid>
            <pubDate>Tue, 28 Nov 2023 04:05:35 GMT</pubDate>
            <description><![CDATA[<h1 id="프록시-proxy">프록시 Proxy</h1>
<p>: 서버와 클라이언트 사이의 중계기로서 대리로 통신을 수행하는 컴퓨터 시스템</p>
<h2 id="위치-보호대상에-따른-분류">위치, 보호대상에 따른 분류</h2>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/382f648e-0e05-48be-b2bf-c29de8642d04/image.png" alt=""></p>
<h3 id="포워드-프록시forward-proxy">포워드 프록시(Forward Proxy)</h3>
<p>내부망에서 클라이언트 다음에 위치 </p>
<p>→ 클라이언트가 외부 서버에 접근하고자 할때, 프록시가 요청과 리소스 응답을 중계함 (요청 포워딩)</p>
<h4 id="기능">기능</h4>
<ol>
<li><p>중계 → 사용자를 대신하여 서버에 접근</p>
</li>
<li><p>클라이언트 보호
2-1. 실제 사용자의 IP주소 숨김
2-2. 특정사이트 접속 차단</p>
</li>
<li><p>캐싱 : 자주 요구된 정보에 대해 일시적으로 저장하여 응답 
→ 서버 부하 감소로 조회속도 증가</p>
</li>
</ol>
<h3 id="리버스-프록시reverse-proxy">리버스 프록시(Reverse Proxy)</h3>
<p>내부망 밖, 서버 앞에 위치</p>
<p>→ 주로 DMZ에 위치하여 내부망에 있는 서버(WAS, DB)를 보호 
(Web Server가 이 역할을 하기도함)</p>
<h4 id="기능-1">기능</h4>
<ol>
<li><p>중계</p>
</li>
<li><p>로드밸런싱 (특정 서버의 과부하 방지)</p>
</li>
<li><p>서버 보호 
2-1. 실제 서버의 IP주소 숨김
2-2. SSL 대신 사용하여 서버 부담 감소</p>
</li>
<li><p>캐싱 (서버 부하 감소로 조회속도 증가)</p>
</li>
</ol>
<blockquote>
<p><strong>DMZ</strong>
내부 네트워크에 포함되어 있으나, 외부에서 접근할 수 있는 구간
→ 일반적으로 인터넷을 통해 외부에 서비스를 제공해야 하는 웹 및 메일 서버 등이 위치하는 구간을 지칭</p>
</blockquote>
<p>&nbsp;</p>
<h3 id="프록시-vs-게이트웨이">프록시 vs 게이트웨이</h3>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">프록시</th>
<th align="center">게이트웨이</th>
</tr>
</thead>
<tbody><tr>
<td align="center">연결 프로토콜</td>
<td align="center">동일 프로토콜로 연결</td>
<td align="center">서로 다른 프로토콜 간 변환 연결</td>
</tr>
<tr>
<td align="center">통신 내용</td>
<td align="center">통신 내용 이해</td>
<td align="center">통신 내용 그대로 전달</td>
</tr>
<tr>
<td align="center">내용 수정</td>
<td align="center">내용 수정 가능</td>
<td align="center">내용 수정 불허</td>
</tr>
</tbody></table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr>
<h1 id="vpn-virtual-private-network">VPN (Virtual Private Network)</h1>
<p>: 보안을 위해 공중망을 전용망처럼 사용하는 기술 또는 장비</p>
<h3 id="기능-2">기능</h3>
<ol>
<li><p>IP Packet 캡슐화 (데이터 암호화)</p>
</li>
<li><p>IP 주소 암호화
→ Tunneling  : 가상의 터널이 생성되어 사용자 추적을 어렵게 만듬</p>
</li>
</ol>
<ol start="3">
<li>자체 DNS 사용 
→ 사용자가 웹 사이트를 찾아 이동하는 활동을 비밀로 유지</li>
</ol>
<h3 id="한계">한계</h3>
<p>VPN 서버와 웹 서비스 사이의 연결은 암호화 계층이 없기 때문에, 연결을 가로채면 ‘평문’으로 전송되는 데이터를 읽을 수 있다. 이러한 데이터로는 브라우저가 제공하는 데이터 또는 사용하고 있는 기기가 웹 사이트에 제공하는 정보 등이 대표적이다. 소프트웨어 버전 번호, 화면 해상도, 제조사 및 모델 등의 데이터가 모이면 디지털 지문에 가까워진다. 적절한 지식과 툴이 사람이라면 이 정보를 이용해 사용자의 활동을 추적하고 신원을 파악할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/5cc32db7-a222-499e-9006-043c9d1aa7e6/image.png" alt=""></p>
<p>&nbsp;</p>
<h3 id="vpn-vs-프록시">VPN vs 프록시</h3>
<p>프록시 서버와 달리 VPN은 운영 체제 수준에서 동작하므로, 브라우저나 백그라운드 앱에서 발생하는 모든 트래픽을 리다이렉션할 수 있다. 또한, 프록시가 특정 앱만 보호하는 반면, VPN은 장치의 모든 수발신 데이터를 보호한다.</p>
<p>&nbsp;</p>
<hr>
<h1 id="방화벽-firewall">방화벽 Firewall</h1>
<p>: IP, Port, 프로토콜에 대한 보안규칙을 기반으로 트래픽을 필터링하는 네트워크 보안 시스템</p>
<p>→ 인바운드, 아웃바운드 트래픽은 모두 방화벽 통과</p>
<h2 id="종류">종류</h2>
<ul>
<li><p>L3 패킷 필터링 
→ 헤더의 IP, Port, protocol, flag 에 따라 패킷 필터링</p>
</li>
<li><p>L4 상태 유지 검사
→  L3 패킷 필터링에 더해 TCP 연결 상태 정보를 저장하여 패킷 필터링</p>
</li>
<li><p>L4 회선 레벨 게이트웨이
→ 연결 프록시가 어플리케이션 간의 트래픽 중계하며, TCP연결 관장</p>
</li>
<li><p>L5 응용 레벨 게이트웨이
→ 서비스 프록시가 어플리케이션 간의 트래픽 감시</p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="ips">IPS</h2>
<p>(Intrusion Prevention System 침입 방지 시스템)</p>
<p>: 비정상적인 트래픽에 대해 능동적으로 해당 트래픽을 차단, 격리, 교정 등 방어조치를 취하는 보안 솔루션</p>
<p>→ 트래픽 패턴, 이상 징후(임곗값)이나 공격 시그니처(사전 정의된 공격 형태 참조)를 찾아 공격 탐지, 방지</p>
<ul>
<li>보통 방화벽 바로 뒤에서 방화벽과 함께 작동</li>
<li>차세대 방화벽에 포함됨
<img src="https://velog.velcdn.com/images/sua_ahn/post/1ac653f5-8538-447f-b7e5-3e4bef8b0d96/image.png" alt=""></li>
</ul>
<p>&nbsp;</p>
<hr>
<p>참고사이트</p>
<ul>
<li>프록시
<a href="http://www.ktword.co.kr/test/view/view.php">http://www.ktword.co.kr/test/view/view.php</a>?
<a href="https://kr.linkedin.com/posts/mohamedelgamasy_forward-proxy-vs-reverse-proxy-activity-6991703343337246720-RkPY?trk=public_profile_like_viewm_temp1=1829">https://kr.linkedin.com/posts/mohamedelgamasy_forward-proxy-vs-reverse-proxy-activity-6991703343337246720-RkPY?trk=public_profile_like_viewm_temp1=1829</a></li>
<li>VPN
<a href="https://www.itworld.co.kr/topnews/313556">https://www.itworld.co.kr/topnews/313556</a></li>
<li>방화벽
<a href="https://dad-rock.tistory.com/871">https://dad-rock.tistory.com/871</a></li>
<li>IPS
<a href="https://yozm.wishket.com/magazine/detail/1781/">https://yozm.wishket.com/magazine/detail/1781/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAR, WAR, EAR 비교 (Executable Jar, Fat Jar, bootWar)]]></title>
            <link>https://velog.io/@sua_ahn/JAR-WAR-EAR</link>
            <guid>https://velog.io/@sua_ahn/JAR-WAR-EAR</guid>
            <pubDate>Tue, 28 Nov 2023 01:10:47 GMT</pubDate>
            <description><![CDATA[<h1 id="jar-war-ear">JAR, WAR, EAR</h1>
<h2 id="사용-이유">사용 이유</h2>
<p>복잡한 Java 애플리케이션을 단일 압축 파일로 패키징하여 다른 환경 간에 이동시키거나, 다른 어플리케이션의 런타임 시 해당 어플리케이션에 연결하고자 할 때 유용하다. 또한, Java 애플리케이션 배포를 단순화하는 데 도움이 된다.</p>
<h3 id="zip-과의-비교">ZIP 과의 비교</h3>
<p>JAR, WAR, EAR는 모두 ZIP 파일 포맷을 기반으로 하기 때문에 ZIP 유틸리티를 사용하여 내용을 압축하거나 추출할 수 있다.
다만, 특수한 용도에 맞게 부가적인 규약이 정해져있다.</p>
<h2 id="비교">비교</h2>
<h3 id="공통점">공통점</h3>
<p>내부적으로 보면 EAR, JAR 및 WAR 파일은 모두 Java 애플리케이션을 구성하는 다양한 이미지, XML 파일, 속성 파일 및 Java 코드 조각이 포함된 단순한 ZIP 파일이다.</p>
<h3 id="차이점">차이점</h3>
<p>가장 큰 차이점은 서로 다른 환경을 대상으로 한다는 점이며, 
내부 제한 사항 및 요구 사항에 있어 차이가 있다.</p>
<table>
<thead>
<tr>
<th align="center">확장자</th>
<th align="center">EAR</th>
<th align="center">WAR</th>
<th align="center">JAR</th>
</tr>
</thead>
<tbody><tr>
<td align="center">대상 환경</td>
<td align="center">엔터프라이즈 에디션의 자바 플랫폼(Java EE)</td>
<td align="center">Java EE 웹 프로필 호환 애플리케이션 서버</td>
<td align="center">표준 에디션의 자바 플랫폼(Java SE)</td>
</tr>
<tr>
<td align="center">요구 사항</td>
<td align="center">META-INF 폴더에 포함된 application.xml</td>
<td align="center">WEB-INF 폴더에 포함된 web.xml</td>
<td align="center">없음</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/6577dade-1278-4338-999c-f72e559b8443/image.png" alt=""></p>
<p>&nbsp;</p>
<hr>
<h1 id="jar">JAR</h1>
<p>&#39;JAVA ARchive&#39;
JAR 파일에는 독립 실행형 Java 애플리케이션, 배포 가능한 Java 애플릿 또는 Java Runtime Environment 가  링크할 수 있는 Java 라이브러리 등 다양한 구성 요소가 모두 포함되어 있다.</p>
<h2 id="실행-가능한-jar-executable-jar">실행 가능한 Jar (Executable Jar)</h2>
<p>: MANIFEST.MF* 파일 내 Main 클래스 위치(엔트리 포인트)가 지정되어 있고, 외부 의존성까지 포함하여 실제 프로그램으로 실행가능한 Jar</p>
<blockquote>
</blockquote>
<p><strong>Skinny</strong>
코드 편집기에 문자 그대로 입력한 비트만 포함</p>
<blockquote>
</blockquote>
<p><strong>Thin</strong>
Skinny 항목과 애플리케이션의 직접적인 종속성(db 드라이버, 유틸리티 라이브러리 등)이 포함</p>
<blockquote>
</blockquote>
<p><strong>Hollow</strong>
Thin의 반대. 애플리케이션을 실행하는 데 필요한 비트만 포함되어 있으며 애플리케이션 자체는 포함되어 있지 않음. 추후에 애플리케이션을 배포할 수 있도록 사전 패키지된 &quot;애플리케이션 서버&quot;</p>
<blockquote>
</blockquote>
<p><strong>Fat/Uber</strong>
문자 그대로 직접 작성한 비트와 애플리케이션의 직접적인 종속성, 애플리케이션을 &quot;자체적으로&quot; 실행하는 데 필요한 비트가 포함됨
→ Executable Jar
<img src="https://velog.velcdn.com/images/sua_ahn/post/d3b2ea1e-67f6-492f-8da3-376106a1b098/image.png" alt=""></p>
<h3 id="bootjar">bootJar</h3>
<p>war는 jar를 포함할 수 있지만 jar는 다른 jar를 포함하지 못한다. 내장톰캣과 같이 실행에 필요한 외부 jar를 포함하려면 특수한 방법이 필요하다.</p>
<p>이전에는 Fat Jar 방식으로 모든 jar를 class파일로 푼 뒤, 다시 하나의 jar로 만드는 방식도 사용되었다. 그러나 jar간 경계가 사라지고 파일이름이 중복되는 문제가 발생하여 적합하지 않다. </p>
<p>이를 해결하기 위해 SpringBoot는 SpringBoot만의 실행가능한 JAR 구조를 갖는다. Application class는 중첩된 BOOT-INF/classes 디렉토리에 위치하며, Dependency는 중첩된 BOOT-INF/lib 디렉토리에 위치해야 한다.</p>
<pre><code>example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-&lt;spring boot loader classes&gt;
 +-BOOT-INF
    +-classes
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar</code></pre><p>위 구조를 이용하여 spring-boot-starter-web 이나 spring-boot-starter-tomcat 의존성을 추가함으로써 내장 톰캣(tomcat-embed-core)을 사용할 수 있다.</p>
<p>톰캣이 내장된 애플리케이션은 톰캣 구성에 대한 부분의 관심사를 분리하여 개발자에게 애플리케이션단의 코드 작성에 더 집중할 수 있게 해준다.</p>
<blockquote>
<p><strong>META-INF/MANIFEST.MF</strong> 매니페스트 파일
: 코드 버전, 코드의 주요 작성자, 코드를 유지관리하는 조직의 이름과 같은 속성을 포함하여 JAR에 대한 메타데이터가 포함됨</p>
</blockquote>
<h3 id="실행과정">실행과정</h3>
<ol>
<li><code>java -jar xxx.jar</code></li>
<li>org.springframework.boot.loader.jar.JarFile 이 JAR 파일을 인식하고 로드</li>
<li>MANIFEST.MF 인식</li>
<li>Main-Class인 org.springframework.boot.loader.JarLauncher.main() 실행</li>
<li>BOOT-INF/classes/ 인식</li>
<li>BOOT-INF/lib/ 인식</li>
<li>BootApplication.main() 실행</li>
</ol>
<p>&nbsp;</p>
<h3 id="jar-여는-방법">JAR 여는 방법</h3>
<h4 id="압축-열기">압축 열기</h4>
<ul>
<li><code>jar tf</code> 명령어</li>
<li>.zip 으로 확장자 변경 -&gt; 압축 해제 도구 이용</li>
<li><code>unzip -l</code> 명령어</li>
</ul>
<h4 id="내부-파일내용-확인">내부 파일내용 확인</h4>
<ul>
<li>여러 GUI 도구 사용 (ex. JD-GUI) </li>
</ul>
<p>&nbsp;</p>
<hr>
<h1 id="war">WAR</h1>
<p>&#39;Web Application Archive&#39; 또는 &#39;Web Application Resource&#39; </p>
<h3 id="특징">특징</h3>
<ul>
<li>웹 애플리케이션 리소스를 위한 표준 패키지
→ JSP(JavaServer Pages) 가 동작함</li>
<li>bootWar 외엔 외장 톰캣으로만 실행 가능</li>
</ul>
<h3 id="요구사항">요구사항</h3>
<ul>
<li>Java EE 웹 프로필 호환 애플리케이션 서버 필요</li>
<li>WEB-INF 폴더에 포함된 web.xml 파일(배포 설명자)이 필요
→ Spring Framework’s Servlet 3.0 부터 web.xml 대신 WebApplicationInitializer 인터페이스를 구현하여 배포 가능
(Apache Tomcat 7.0부터 지원)</li>
</ul>
<blockquote>
<p><strong>웹 프로필(Web Profile)</strong>
전체 Java EE 플랫폼의 특정 하위 집합으로, 웹 애플리케이션 개발에 관련된 것들을 묶어서 정의함</p>
</blockquote>
<ul>
<li>목적은 개발자가 간단한 서블릿 컨테이너(예: Tomcat 또는 Jetty) 내에서 사용할 수 있는 보다 가벼운 애플리케이션을 만들 수 있도록 하는 것</li>
<li>Apache TomEE 와 같은 제품으로 구현되거나 Eclipse Jetty 와 추가 기능을 사용하여 구현됨
<img src="https://velog.velcdn.com/images/sua_ahn/post/1a7ecc96-4a3e-4e46-bf10-942b76777bb8/image.png" alt=""></li>
</ul>
<p>&nbsp;</p>
<h2 id="bootwar">bootWar</h2>
<p>실행할 때는 필요하지만, 웹 컨테이너를 배포하는데 필요없는 내장 톰캣과 같은 의존성은 WEB-INF/lib-provided 안에 위치한다.</p>
<blockquote>
<p>3가지 런처
: JarLauncher, WarLauncher, PropertiesLauncher
→ 목표는 nestsed jar 파일을 읽어들이는 것
→ Launcher는 Main-Class에 설정해주어야 함 (Maven 플러그인이 자동으로 해줌)</p>
</blockquote>
<pre><code>example.war
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-&lt;spring boot loader classes&gt;
 +-WEB-INF
    +-classes
    |  +-com
    |     +-mycompany
    |        +-project
    |           +-YourClasses.class
    +-lib
    |  +-dependency1.jar
    |  +-dependency2.jar
    +-lib-provided
       +-servlet-api.jar
       +-dependency3.jar</code></pre><p>&nbsp;</p>
<hr>
<h1 id="ear">EAR</h1>
<p>&#39;Enterprise Archive&#39;</p>
<h3 id="특징-1">특징</h3>
<ul>
<li>WAR 파일과 JAR 파일을 포함하는 상위 집합</li>
<li>Java EE 기반 엔터프라이즈 애플리케이션의 경우 .ear 확장자를 사용</li>
</ul>
<h3 id="요구사항-1">요구사항</h3>
<ul>
<li>Java EE(Java Platform, Enterprise Edition) 또는 EE(Jakarta Enterprise Edition) 호환 애플리케이션 서버 ( 예: WebSphere 또는 JBoss )가 필요</li>
<li>META-INF라는 폴더에 포함된 application.xml 파일 필요</li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr>
<p>참고사이트</p>
<ul>
<li><p>차이점
<a href="https://www.theserverside.com/feature/What-are-the-differences-between-EAR-JAR-and-WAR-files">https://www.theserverside.com/feature/What-are-the-differences-between-EAR-JAR-and-WAR-files</a>
<a href="https://hye0-log.tistory.com/27">https://hye0-log.tistory.com/27</a>
<a href="https://velog.io/@gongmeda/JAR-vs-WAR?ref=codenary">https://velog.io/@gongmeda/JAR-vs-WAR?ref=codenary</a>
<a href="https://simuing.tistory.com/269">https://simuing.tistory.com/269</a>
<a href="https://stackoverflow.com/questions/5871053/difference-between-jar-and-war-in-java">https://stackoverflow.com/questions/5871053/difference-between-jar-and-war-in-java</a>
<a href="https://dev.to/martygo/what-is-the-difference-between-a-jar-and-a-war-file-402a">https://dev.to/martygo/what-is-the-difference-between-a-jar-and-a-war-file-402a</a></p>
</li>
<li><p>bootJar, bootWar
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html">https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html</a></p>
</li>
<li><p>Jar 종류
<a href="https://stackoverflow.com/questions/19150811/what-is-a-fat-jar">https://stackoverflow.com/questions/19150811/what-is-a-fat-jar</a>
<a href="https://medium.com/@lalida_a/fat-jar-%E0%B8%99%E0%B9%89%E0%B8%AD%E0%B8%87%E0%B8%88%E0%B8%B2%E0%B8%A3%E0%B9%8C%E0%B8%AD%E0%B9%89%E0%B8%A7%E0%B8%99-6a7de3ad0864">https://medium.com/@lalida_a/fat-jar-%E0%B8%99%E0%B9%89%E0%B8%AD%E0%B8%87%E0%B8%88%E0%B8%B2%E0%B8%A3%E0%B9%8C%E0%B8%AD%E0%B9%89%E0%B8%A7%E0%B8%99-6a7de3ad0864</a>
<a href="https://www.theserverside.com/definition/JAR-file-Java-ARchive">https://www.theserverside.com/definition/JAR-file-Java-ARchive</a></p>
</li>
<li><p>내장톰캣
<a href="https://hyuuny.tistory.com/210">https://hyuuny.tistory.com/210</a></p>
</li>
<li><p>WAR의 web.xml
<a href="https://medium.com/@SlackBeck/spring-boot-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%84-war%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%A0-%EB%95%8C-%EC%99%9C-springbootservletinitializer%EB%A5%BC-%EC%83%81%EC%86%8D%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B1%B8%EA%B9%8C-a07b6fdfbbde">https://medium.com/@SlackBeck/spring-boot-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%84-war%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%A0-%EB%95%8C-%EC%99%9C-springbootservletinitializer%EB%A5%BC-%EC%83%81%EC%86%8D%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B1%B8%EA%B9%8C-a07b6fdfbbde</a></p>
</li>
<li><p>외장 내장 톰캣 차이
<a href="https://thxwelchs.github.io/EmbeddedTomcat%EA%B3%BCTomcat%EC%9D%98%EC%B0%A8%EC%9D%B4/">https://thxwelchs.github.io/EmbeddedTomcat%EA%B3%BCTomcat%EC%9D%98%EC%B0%A8%EC%9D%B4/</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 계층 모델(2)]]></title>
            <link>https://velog.io/@sua_ahn/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B82</link>
            <guid>https://velog.io/@sua_ahn/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B82</guid>
            <pubDate>Tue, 14 Nov 2023 08:33:43 GMT</pubDate>
            <description><![CDATA[<h1 id="네트워크-계층-모델">네트워크 계층 모델</h1>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/55c6194f-eef3-48e2-9ea4-413dc1758d93/image.png" alt=""></p>
<h2 id="l3-네트워크-계층-network-layer">L3 네트워크 계층 Network Layer</h2>
<h3 id="기능">기능</h3>
<ol>
<li><p>패킷화 (Packetizing)</p>
<blockquote>
<p><strong>패킷 (Packet)</strong>
: 통신의 기본 단위
→ TCP 세그먼트나 데이터그램을 패킷 헤더와 트레일러로 감싼 것 
→ 헤더에는 출발지와 목적지의 IP주소가 포함됨</p>
<p><strong>IPv4</strong> (Internet Protocol Version 4)
: 32비트 길이의 네트워크 인터페이스 식별자
→ 0.0.0.0 ~ 255.255.255.255 의 값을 가짐</p>
</blockquote>
</li>
<li><p>포워딩 (Forwarding)
라우터 내 패킷을 전달하는 작업</p>
</li>
<li><p>라우팅 (Routing)
목적지까지의 경로 구축 작업</p>
</li>
</ol>
<h3 id="장비">장비</h3>
<ul>
<li>라우터 (Router)
: 각기 독립된 네트워크들을 연결하여 경로를 설정하고, 패킷을 전달하는 장치</li>
</ul>
<ul>
<li>L3 스위치
: VLAN(Virtual LAN) 기능으로 네트워크 분할을 처리하는 장비</li>
</ul>
<hr>
<h2 id="l2-데이터링크-계층-data-link-layer">L2 데이터링크 계층 Data Link Layer</h2>
<h3 id="기능-1">기능</h3>
<ol>
<li>인접한 네트워크 장비 간에 데이터를 주고 받음</li>
<li>물리 계층에서 발생하는 오류를 감지하고 수정</li>
</ol>
<blockquote>
<p>이더넷 (Ethernet)
: LAN을 유선으로 구현하는 기술
→ 실제 물리 회선을 통해 프레임을 주고 받기 위한 프로토콜</p>
</blockquote>
<blockquote>
<p>이더넷 프레임
MAC(Media Access Control) 주소</p>
</blockquote>
<h3 id="장비-1">장비</h3>
<ul>
<li><p>스위치 (Switch)
: 여러 컴퓨터를 서로 연결하는 장치(허브와 유사)로, MAC주소를 이용하여 데이터를 필요로 하는 컴퓨터에만 전송</p>
</li>
<li><p>L2 스위치
: 스위칭 허브로 가장 많이 사용되는 네트워크 중계기기</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/9180f425-1a21-475a-8528-bc6a75d4939c/image.png" alt=""></p>
<hr>
<h2 id="l1-물리-계층-physical-layer">L1 물리 계층 Physical Layer</h2>
<h3 id="기능-2">기능</h3>
<p>데이터와 신호 간의 변환 &amp; 신호의 이동</p>
<h3 id="장비-2">장비</h3>
<ul>
<li><p>랜선 (Ethernet Cable)
: 네트워크 연결용 전선</p>
</li>
<li><p>랜카드 (Network Interface Card, NIC)
: 각 랜카드마다 소유한 MAC주소를 사용하여 주소 할당 시스템을 제공하고, 네트워크 매개체로 물리적인 접근을 가능하게 함
→ 직렬화(Serialization) 즉, 신호 변환</p>
</li>
<li><p>리피터 (Repeater)
: 전기 신호를 복원하고 증폭하는 기능을 가진 중계장비</p>
</li>
<li><p>허브 (Hub)
: 여러 컴퓨터를 서로 연결하는 장치로, 데이터를 연결된 모든 기기에 전달 (브로드캐스트 통신 방식)</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/b4f565a0-980c-4ac8-a4ff-a97e24aefcea/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/87d3a98c-f7dd-4ee6-ae66-822b0120e639/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Web] HTTP & WAS & Servlet]]></title>
            <link>https://velog.io/@sua_ahn/Web-HTTP-WAS-Servlet</link>
            <guid>https://velog.io/@sua_ahn/Web-HTTP-WAS-Servlet</guid>
            <pubDate>Tue, 14 Nov 2023 08:17:24 GMT</pubDate>
            <description><![CDATA[<h1 id="http">HTTP</h1>
<p>Hypertext Transfer Protocol
: 하이퍼텍스트 링크를 사용하여 네트워크 장치 간에 정보를 전송하도록 설계된 애플리케이션 계층 프로토콜</p>
<blockquote>
<p>Hypertext
: 웹문서와 연결된 글</p>
</blockquote>
<p>Protocol
: 통신 시  데이터의 교환 방식을 정의하는 규칙 체계</p>
<h2 id="url">URL</h2>
<p>Uniform Resource Locator
: 네트워크 상에서 자원의 <strong>위치</strong>를 가리키는 문자열
→ 자원의 위치를 옮기면 해당 URL 사용 불가</p>
<blockquote>
<p>URI (Uniform Resource Identifier)
: 자원에 대한 고유 식별자
URN (Uniform Resource Name)
: 자원의 고유 이름</p>
</blockquote>
<h3 id="구조">구조</h3>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/47cc90ad-0c98-46a2-9742-1e9aeb50951f/image.png" alt=""></p>
<ul>
<li>Scheme
: 리소스에 접근하는 데 사용할 프로토콜</li>
<li>Host
: 접근할 대상(서버)의 호스트 명</li>
<li>Path: 접근할 대상(서버)의 경로에 대한 상세 정보<ul>
<li>쿼리스트링: <code>?</code> 뒤에 이름과 값으로 쌍을 이루는 요청 파라미터
→ 요청 파라미터가 여러 개이면 <code>&amp;</code>로 연결</li>
<li>프래그먼트: <code>#</code> 뒤에 오는 파라미터 
→ HTML 내 해당 ID 위치로 스크롤 이동</li>
</ul>
</li>
</ul>
<h2 id="http-요청-메서드">HTTP 요청 메서드</h2>
<p>: Server에 요청할 수 있는 방법</p>
<ol>
<li><p>get 조회 요청</p>
<ul>
<li>요청을 전송할 때 필요한 데이터를 쿼리스트링을 통해 전송</li>
<li>입력 정보가 url에 적혀있으므로 보안에 취약</li>
<li>캐시 옵션을 지정할 수 있음</li>
<li>길이 제한 있음</li>
<li>바이너리 파일(이미지, 오디오 등) 전송 불가</li>
<li>브라우저 히스토리에 남음</li>
</ul>
</li>
<li><p>post 생성 요청</p>
<ul>
<li>Body에 데이터를 담아 요청</li>
<li>&#39;safe 하지 않고 idempotent 하지도 않는 메서드&#39;</li>
</ul>
</li>
</ol>
<blockquote>
<ul>
<li>HTTP 메서드는 서버의 상태를 변경하지 않을 때 &quot;안전&quot;하다고 한다.</li>
<li>반복해서 호출할 때 동일한 결과의 형식을 생성하는 작업을 &quot;멱등적&quot;이다고 한다.</li>
</ul>
</blockquote>
<ol start="3">
<li><p>put 생성 또는 대체 요청
: 새로운 리소스를 생성(create)하거나 이미 존재할 경우 대체(replace) </p>
<ul>
<li>&#39;safe 하지는 않지만 idempotent 한 메서드&#39; </li>
</ul>
</li>
<li><p>delete 삭제 요청</p>
</li>
<li><p>patch 부분 수정 요청</p>
<ul>
<li>&#39;Support for PATCH in browsers, servers, and web application frameworks is not universal. IE8, PHP, Tomcat, Django, and lots of other software have missing or broken support for it.&#39;</li>
</ul>
</li>
</ol>
<hr>
<h1 id="was">WAS</h1>
<p>Web Application Server
: 웹 기반 프로그램을 제공하는 시스템</p>
<p>→ 크게 <strong>웹 서버 기능</strong>와 <strong>컨테이너 기능</strong>으로 구성 (서버의 기능을 분리하여 처리)</p>
<blockquote>
<p>대표적인 WAS : Apache Tomcat</p>
</blockquote>
<h2 id="web-server">Web Server</h2>
<p>: HTTP 프로토콜을 기반으로, 웹 클라이언트로부터 요청을 받고 처리 결과를 응답하는 기능을 담당하며, 정적 콘텐츠 제공 (Presentation logic)</p>
<h2 id="web-container">(Web) Container</h2>
<p>: 동적 콘텐츠 처리 &amp; 데이터베이스와의 연동 작업 처리 (Business logic)</p>
<h3 id="역할">역할</h3>
<ul>
<li>Servlet과 웹 서버의 통신 지원 </li>
<li>Servlet의 라이프 사이클 관리</li>
<li>multi threading 지원</li>
<li>보안 관리</li>
<li>JSP 지원</li>
</ul>
<h3 id="종류">종류</h3>
<ul>
<li><p><strong>Servlet 컨테이너</strong>
: Servlet을 이용해 작성된 프로그램을 실행, 관리해주는 주체</p>
</li>
<li><p><strong>JSP 컨테이너</strong> 
: Servlet 컨테이너에 Servlet으로의 변환 과정이 추가된 서버 프로그램</p>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>JSP 
: HTML 내에 JAVA 코드가 삽입되어 있어, 동적인 contents를 생성하는 기술 
→ Servlet 클래스의 자바 소스로 변환된 후 compile 되어 실행됨</li>
</ul>
<hr>
<h1 id="servlet">Servlet</h1>
<p>Server + Applet
: servlet class의 구현 규칙을 지킨 자바 프로그램(API)</p>
<p>→ JAVA 언어를 기반으로 HTML 태그가 섞여 있어, 동적인 contents를 생성</p>
<blockquote>
<p><strong>Server</strong>
: 클라이언트의 요청을 받아 서비스나 데이터를 제공하는 프로그램(소프트웨어) 또는 컴퓨터(하드웨어)
<strong>Applet</strong>
: 큰 프로그램에 포함되어 작은 기능을 수행하는 프로그램</p>
</blockquote>
<h4 id="servlet으로-요청-응답">Servlet으로 요청, 응답</h4>
<p>apache와 같은 웹 서버가 사용자로부터 Servlet에 대한 요청을 받으면 Servlet 컨테이너(tomcat)에게 이 요청을 넘긴다. 요청을 넘겨받은 컨테이너는 HTTP Request와 HTTP Response 객체를 만들어 이를 인자로 Servlet doPost()나 doGet() method 중 하나를 호출</p>
<hr>
<p>*참고사이트
<a href="https://yeon-kr.tistory.com/121">웹서버란</a>
<a href="https://yeon-kr.tistory.com/135">서블릿이란</a>
<a href="https://dataonair.or.kr/db-tech-reference/d-lounge/technical-data/?mod=document&amp;uid=235934">WAS의 동작</a>
<a href="https://opentutorials.org/module/3569/21238">JSP/서블릿</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[가상머신에서 아파치+톰캣 연동 (VMware, CentOS7)]]></title>
            <link>https://velog.io/@sua_ahn/%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0%EC%97%90%EC%84%9C-%EC%95%84%ED%8C%8C%EC%B9%98%ED%86%B0%EC%BA%A3-%EC%97%B0%EB%8F%99-VMware-CentOS7</link>
            <guid>https://velog.io/@sua_ahn/%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0%EC%97%90%EC%84%9C-%EC%95%84%ED%8C%8C%EC%B9%98%ED%86%B0%EC%BA%A3-%EC%97%B0%EB%8F%99-VMware-CentOS7</guid>
            <pubDate>Mon, 13 Nov 2023 08:37:20 GMT</pubDate>
            <description><![CDATA[<h2 id="가상머신-설치">가상머신 설치</h2>
<p><a href="https://customerconnect.vmware.com/en/downloads/details?downloadGroup=WKST-PLAYER-1750&amp;productId=1377&amp;rPId=111473">VMware 설치</a>
VirtualBox보다 설정이 편하고, 드래그 앤 드롭으로 파일 전송 가능!</p>
<h2 id="리눅스-설치">리눅스 설치</h2>
<p><a href="http://isoredirect.centos.org/centos/7/isos/x86_64/">CentOS7 설치</a></p>
<ul>
<li><p>CentOS-7-x86_64-DVD-숫자.iso 로 다운로드
(DVD 파일 : 일반적인 패키지가 포함되어 있고 설치 과정 중에 원하는 패키지를 선택해서 함께 설치할 수 있음)</p>
</li>
<li><p>VMware 실행 후 &#39;Create a new virtual machine&#39;</p>
</li>
<li><blockquote>
<p>리눅스 설치 파일 선택해서 설치 진행</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="서버-세팅">서버 세팅</h2>
<h3 id="1-타임존-설정">1. 타임존 설정</h3>
<pre><code class="language-shell">#타임존 확인
ls -l /etc/localtime
date
timedatectl

#타임존 서울로 변경 
timedatectl set-timezone Asia/Seoul</code></pre>
<h3 id="2-방화벽-설정">2. 방화벽 설정</h3>
<pre><code class="language-shell">#방화벽 구동 확인
firewall-cmd --state

#열린 포트 확인
firewall-cmd --zone=public --list-all

#포트 개방 (80, 8080, 443, 22)
firewall-cmd --permanent --zone=public --add-port=8080/tcp

#적용
firewall-cmd --reload</code></pre>
<h3 id="3-putty-접속">3. puTTY 접속</h3>
<pre><code class="language-shell">#ip 주소 확인
ifconfig</code></pre>
<hr>
<h2 id="아파치-톰캣-설치">아파치, 톰캣 설치</h2>
<p>아파치: httpd-2.4.37
톰캣: apache-tomcat-8.0.33</p>
<p><em>JDK 설치 및 환경변수 설정 후 설치할 것!
\</em>설치 파일들은 따로 구글링</p>
<blockquote>
<ul>
<li>HTTPD : 아파치 웹 서버의 프로세스 이름</li>
</ul>
</blockquote>
<ul>
<li>APR(Apache Portable Runtime) : 기본 프로세스 처리, OS 지원 기능 등을 제공하는 라이브러리</li>
<li>PCRE(Perl Compatible Regular Expressions) : 펄 호환 정규 표현식으로, 정규식 패턴 일치를 구현하는 함수의 집합</li>
<li>mod_jk : Tomcat 서블릿 컨테이너를 웹서버와 연결하는 아파치 모듈</li>
</ul>
<pre><code class="language-shell">yum update -y

#1. gcc, gcc 컴파일러 설치
yum install -y gcc gcc-c++

#2. apr 설치
cd /usr/local/src/apr-1.6.5/
./configure --prefix=/usr/local/apache/apr
make
make install

#3. expat-devel 설치
yum install -y expat-devel

#4. aprutil 설치
cd /usr/local/src/apr-util-1.6.1/
./configure --prefix=/usr/local/apache/aprutil --with-apr=/usr/local/apache/apr/
make
make install

#5. pcre 설치
 cd /usr/local/src/pcre-8.42/
 ./configure --prefix=/usr/local/apache/pcre-8.42 --enable-utf
 make
 make install

#7. apache 설치
cd /usr/local/src/httpd-2.4.37/
./configure --prefix=/usr/local/apache --enable-module=so --enable-so --enable-mods-shared=ssl --with-ssl=/usr/local/openssl --enable-ssl=shared --with-apr=/usr/local/apache/apr --with-apr-util=/usr/local/apache/aprutil --with-pcre=/usr/local/apache/pcre-8.42
make
make install

#8. mod_jk 설치
cd /usr/local/src/tomcat-connectors-1.2.40-src/native/
./configure --with-apxs=/usr/local/apache/bin/apxs
make
make install

#9. mod_deflate 모듈 추가  
yum install -y zlib zlib-devel
cd /usr/local/apache/bin/
./apxs -i -a -c /usr/local/src/httpd-2.4.37/modules/filters/mod_deflate.c

#10. libxml2-devel 설치
yum install -y libxml2-devel

#11. security2 설치
cd /usr/local/src/modsecurity-2.9.1/
./configure --with-apxs=/usr/local/apache/bin/apxs --with-pcre=/usr/local/apache/pcre-8.42 --with-apr=/usr/local/apache/apr --with-apu=/usr/local/apache/aprutil
make
make install

#12. apache-tomcat 폴더 생성
cd /usr/local/
mkdir apache-tomcat
cd /usr/local/src/
mv apache-tomcat-8.0.33 ../apache-tomcat/프로젝트-8.0.33
</code></pre>
<h3 id="구동-테스트">구동 테스트</h3>
<pre><code class="language-shell">#아파치 구동 -&gt; 80포트로 접속
#apache/bin 경로에서
./httpd -k start
./httpd -k stop

#톰캣 구동 -&gt; 8080포트로 접속
#apache-tomcat의 bin 디렉토리에서
./startup.sh
./shutdown.sh</code></pre>
<p>*service나 systemctl 명령어로 구동하려면 yum install httpd -y 로 아파치 설치해야 함 (설치 시 명령어 자동 등록됨)</p>
<h3 id="소유권-수정">소유권 수정</h3>
<pre><code class="language-shell">su -
chown -R 본인id /usr/local/apache-tomcat</code></pre>
<p>*오류로 아파치 재설치 시
재컴파일 전 make clean
yum list installed | grep 으로 설치한거 확인</p>
<hr>
<h2 id="연동-설정">연동 설정</h2>
<blockquote>
<h4 id="연동-이유">연동 이유</h4>
</blockquote>
<p>1) 여러 Tomcat 서버 간의 <strong>로드 밸런싱</strong> 및 <strong>페일오버</strong> 기능 사용 (고부하 환경의 성능을 향상시키기 위해)
2) <strong>정적 콘텐츠</strong>의 빠른 처리 및 전달 (서블릿 컨테이너와 웹서버의 역할 분리)
3) Apache에서 사용할 수 있는 몇 가지 추가 기능 사용 (Ex. Virtual Host)</p>
<h3 id="아파치-설정">아파치 설정</h3>
<h4 id="confhttpdconf">conf/httpd.conf</h4>
<pre><code>##추가
LoadModule jk_module modules/mod_jk.so

#해당 모듈이 존재한다면 실행하는 if 구문
&lt;IfModule mod_jk.c&gt;
    JkWorkersFile           conf/workers.properties
    JkLogFile               logs/mod_jk.log
    JkLogLevel              info
    JkLogStampFormat        &quot;[%a %b %d %H:%M:%S %Y] &quot;
    JkRequestLogFormat      &quot;%w %V %T&quot;
&lt;/IfModule&gt;

##수정
ServerName localhost

&lt;Directory /&gt;
    #해당 디렉터리 내에서 심볼릭 링크를 가능하게 함
    Options FollowSymLinks
    AllowOverride none
    #Require all denied
    #웹 서버의 최상위 경로부터 모든 하위 경로까지 클라이언트의 액세스 허용
    Require all granted
&lt;/Directory&gt;

# Virtual hosts 하나의 서버에서 여러 웹서비스를 구동할때 사용
Include conf/extra/httpd-vhosts.conf</code></pre><h4 id="confextrahttpd-vhostsconf">conf/extra/httpd-vhosts.conf</h4>
<pre><code>&lt;VirtualHost *:80&gt;

    ServerAdmin webmaster@dummy-host.example.com
    DocumentRoot &quot;/usr/local/apache-tomcat/프로젝트이름/webapps&quot;
    ServerName localhost

    JkMount /* worker1

&lt;/VirtualHost&gt;</code></pre><h4 id="confworkersproperties-생성">conf/workers.properties 생성</h4>
<pre><code class="language-shell">#웹서버로부터의 서블릿 요청을 처리하기 위해 동작하는 tomcat 인스턴스
worker.list=worker1
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009</code></pre>
<h3 id="톰캣-설정">톰캣 설정</h3>
<h4 id="confserverxml">conf/server.xml</h4>
<pre><code class="language-xml">#특정 port로 들어오는 내용을 수신 -&gt; 톰캣 포트 변경 가능 
#but, 1024 이하의 Well known 포트는 아무 사용자나 수정할 수 없음
#AJP 프로토콜(Apache Jserv Protocol)
&lt;Connector port=&quot;8009&quot; protocol=&quot;AJP/1.3&quot; redirectPort=&quot;8443&quot; address=&quot;0.0.0.0&quot; /&gt;


#Engine에 관련된 가상호스트를 정의
#appBase의 상대경로는 tomcat의 설치 위치
#unpackWARs 속성은 WAR 파일을 풀어서 사용할지 여부를 설정
#autoDeploy 속성은 tomcat 가동중 어플리케이션을 배치한 경우 자동으로 배포할지 설정
&lt;Host name=&quot;localhost&quot;  appBase=&quot;webapps&quot;
            unpackWARs=&quot;true&quot; autoDeploy=&quot;true&quot;&gt;

    #웹 어플리케이션을 의미. docBase에서 webapps 폴더 혹은 war 파일을 지정하여 사용.
    #Context 태그가 Host 안에 없을 경우 기본적으로 webapps 하위의 Root 디렉터리를 기본 path로 설정(path=&quot;/&quot;  war파일명은 ROOT.war)
    #reloadable 속성은 WEB-INF/classes 및 WEB-INF/lib 디렉토리에 변경이 발생할 때 자동 반영 여부를 결정
    &lt;Context path=&quot;&quot; docBase=&quot;프로젝트.war&quot; reloadable=&quot;true&quot;/&gt;</code></pre>
<hr>
<h2 id="결과">결과</h2>
<ul>
<li><p>톰캣 접속 주소
localhost:80
IP주소:80</p>
</li>
<li><p>배포한 프로젝트 접속 주소
localhost/프로젝트
IP주소/프로젝트</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Telnet & SSH & SSL]]></title>
            <link>https://velog.io/@sua_ahn/Telnet-SSH-SSL</link>
            <guid>https://velog.io/@sua_ahn/Telnet-SSH-SSL</guid>
            <pubDate>Thu, 26 Oct 2023 07:26:43 GMT</pubDate>
            <description><![CDATA[<h1 id="telnet">Telnet</h1>
<p>(Teletype / Telecommunication / Terminal Network)
: 원격 접속 프로토콜
사용자가 원격 시스템에 로그인하여 명령을 실행할 수 있도록 하는 네트워크 프로토콜</p>
<h2 id="특징">특징</h2>
<ul>
<li><p>사용자 계정과 비밀번호로 원격 로그인</p>
</li>
<li><p>트래픽 암호화 X  패킷 스니핑*에 취약</p>
</li>
</ul>
<blockquote>
<p>Packet Sniffing 
: 네트워크 통신을 몰래 도청하는 행위로, WireShark 같은 패킷 분석 프로그램을 이용하면 쉽게 원격 접속 과정에서 옮겨지는 비밀번호나 파일 내용 등의 데이터를 탈취할 수 있음</p>
</blockquote>
<ul>
<li>NVT (Network Virtual Terminal)
: 송신 호스트로부터 수신 호스트로 데이터를 전송할 때 두 시스템에서의 데이터 양식이 다르기 때문에 데이터를 변환시키는 가상 장치</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/b89b1e2d-31a9-42e5-a8c6-2cfe9d0a758f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/b0bd922e-8bb3-48d9-9358-cfcce4906744/image.png" alt=""></p>
<blockquote>
<p>Network Bandwitdh(대역폭)
: 네트워크에서 특정 시간 내에 전송될 수 있는 데이터의 최대 용량</p>
</blockquote>
<hr>
<h1 id="ssh-secure-shell">SSH (Secure Shell)</h1>
<p>: 네트워크를 통한 원격 호스트 연결 과정을 보호하기 위해 고안됨</p>
<h2 id="작동-방식">작동 방식</h2>
<h3 id="대칭키세션키">대칭키(세션키)</h3>
<p>: 서버와 클라이언트가 공유하는 대칭키로 정보 암호화
→ 대칭키의 교환을 위해 임시 비대칭키 이용</p>
<h3 id="비대칭키공개키">비대칭키(공개키)</h3>
<p>: 함께 생성된 공개키와 개인키 (Key Pair)만 서로 해석 가능
→ 연결 상대 인증</p>
<h3 id="해시-알고리즘">해시 알고리즘</h3>
<p>: 데이터 패킷과 이 패킷으로 생성된 해시값을 함께 전송하여, 사전 협상한 알고리즘으로 해시값의 진위 여부 확인
→ 패킷의 무결성 확인 → 스푸핑(위변조) 방지</p>
<p>&nbsp;</p>
<hr>
<h1 id="ssl-secure-sockets-layer">SSL (Secure Sockets Layer)</h1>
<p>: 브라우저와 서버 사이에 보안 통신을 하기 위한 프로토콜
→ 넷스케이프사의 SSL 3.0 → IETF의 TLS 1.0 (Transport Layer Security)</p>
<h2 id="ssl-인증서">SSL 인증서</h2>
<p>: 클라이언트와 서버간의 통신을 제3자가 보증해주는 전자화된 문서</p>
<h3 id="기능">기능</h3>
<ul>
<li>신뢰할 수 있는 서버임을 보장</li>
<li>공개키를 클라이언트에게 제공</li>
</ul>
<h3 id="구성">구성</h3>
<ul>
<li>서비스 정보 (인증서를 발급한 CA, 서비스의 도메인 등등)</li>
<li>서버 측 공개키 (공개키의 내용, 공개키의 암호화 방법)</li>
</ul>
<h2 id="cacertificate-authority-혹은-root-certificate">CA(Certificate authority) 혹은 Root Certificate</h2>
<p>: 접속한 서버가 클라이언트가 의도한 서버가 맞는지 보장하는 인증기관
→ CA는 자신의 비공개키를 이용해서 서버의 인증서를 암호화하여 발급
(공인 CA를 통해 인증서를 구입해야 브라우저에 경고가 출력되지 않음)</p>
<h2 id="ssl-handshake">SSL Handshake</h2>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/b24826e1-c118-4255-bc79-7a05dbb7e7e9/image.png" alt=""></p>
<p>1~2. 암호화 방식 협상 
➕ 서버는 클라이언트에게 인증서 제공
➕ 랜덤데이터
&nbsp;
3. 브라우저는 서버 인증서를 발급한 CA가 내장된 CA 리스트에 있는지 확인 후, 해당 CA의 공개키로 인증서를 복호화 
➕ 클라이언트가 서버의 랜덤 데이터와 자신의 랜덤 데이터를 조합해서 pre-master secret 생성</p>
<blockquote>
<p>CA의 공개키로 인증서를 복호화할 수 있음
= 인증서가 CA의 비공개키로 암호화된 것임
= 인증서가 CA에 의해서 발급된 것임</p>
</blockquote>
<ol start="4">
<li><p>인증서에 들어있는 공개키로 pre-master secret을 암호화해서 서버로 전송</p>
</li>
<li><p>서버는 자신의 비공개키로 pre-master secret 복호화 </p>
</li>
<li><p>서버와 클라이언트는 pre-master key로    master secret 생성 
→ master secret으로 session key 생성</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 계층 모델(1)]]></title>
            <link>https://velog.io/@sua_ahn/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B81</link>
            <guid>https://velog.io/@sua_ahn/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B81</guid>
            <pubDate>Thu, 26 Oct 2023 07:20:44 GMT</pubDate>
            <description><![CDATA[<h1 id="네트워크-계층-모델">네트워크 계층 모델</h1>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/75ae5324-dad4-41d7-bd08-d9a750cb0da3/image.png" alt=""></p>
<h2 id="l7-응용-계층-application-layer">L7 응용 계층 Application Layer</h2>
<p>In OSI 7 Layer Model
사용자와 직접 상호작용 (네트워크 관련 서비스 제공)
사용자로부터 받은 요청 전달, 사용자가 활용할 데이터를 응답</p>
<p>In TCP/IP Model
필요한 경우 표현 계층과 세션 계층의 기능 포함</p>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/58e93016-5c9e-4325-88f1-5e6902b01b84/image.png" alt=""></p>
<hr>
<h2 id="l6-표현-계층-presentation-layer">L6 표현 계층 Presentation Layer</h2>
<h3 id="기능">기능</h3>
<ol>
<li><p>데이터 포맷 해석 및 변환
Ex) 수신 데이터가 이미지인지 동영상인지 텍스트인지 구분 
Ex) 바이너리 형식의 데이터를 ASCII코드나 UNICODE 형식으로 변환</p>
</li>
<li><p>데이터 압축 </p>
<ul>
<li>압축 알고리즘(Run-length encoding, Huffman coding 등) 사용</li>
<li>JPEG, MPEG 등의 프로토콜 사용</li>
</ul>
</li>
<li><p>암호화</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/df724cb4-f6ca-4ebe-af2c-c8c8adc327e4/image.png" alt=""></p>
<hr>
<h2 id="l5-세션-계층-session-layer">L5 세션 계층 Session Layer</h2>
<h3 id="기능-1">기능</h3>
<ol>
<li>세션* 연결의 설정, 관리, 종료</li>
<li>토큰을 이용한 대화 제어<ul>
<li>데이터 토큰 : 반이중 통신의 경우, 데이터를 전송할 수 있는 권리 제공</li>
<li>해제 토큰 : 세션 연결 해제를 제어하기 위해 사용</li>
<li>동기 토큰 : 대화 중간 동기화* 지점에 삽입
   → 전송 오류 시, 동기점 이후로 재동기화 수행</li>
</ul>
</li>
</ol>
<blockquote>
<ul>
<li>세션 (Session) 
: 일정 기간 사용자와의 연결 상태를 저장, 유지하는 기술</li>
<li>토큰 (Token)
: 권한을 부여하는 특수 메시지</li>
<li>동기화 (Synchronization) 
: 송신측과 수신측이 정확히 통신할 수 있게 시간을 맞추는 것 또는 데이터 일치화</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/b4cfd3db-7b52-48f2-985a-c43de8b93ba7/image.png" alt=""></p>
<hr>
<h2 id="l4-전송-계층-transport-layer">L4 전송 계층 Transport Layer</h2>
<h3 id="공통-기능">공통 기능</h3>
<ol>
<li>목적지, 출발지의 포트 번호로 프로세스 구분</li>
<li>오류 검출
패킷이 훼손되었는지 체크섬 필드로 확인</li>
</ol>
<h3 id="udp-user-datagram-protocol">UDP (User Datagram Protocol)</h3>
<ul>
<li>연결 수립 및 확인 응답을 하지 않아 빠르게 통신 가능
→ 데이터가 작은 DNS나, 응답이 필요없는 스트리밍, 브로드캐스트에 사용</li>
<li>Application 단에서 데이터 분할</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/a9ceff50-3a73-48c0-822c-75c334dd1cc7/image.png" alt=""></p>
<h3 id="tcp-transport-control-protocol">TCP (Transport Control Protocol)</h3>
<h4 id="기능-2">기능</h4>
<ol>
<li>세그먼트로 분할
→ 세그먼트가 동일한 순서로 도착한다는 보장이 없으므로 각각 일련번호(Sequence Number) 부여</li>
</ol>
<p><em>*Acknowledgment Number : 상대로부터 받아야 하는 다음 TCP 세그먼트 번호</em>
<img src="https://velog.velcdn.com/images/sua_ahn/post/89f8d7f7-4f80-48bc-9c80-1ae8fba4a449/image.png" alt=""></p>
<blockquote>
<p>분할하는 이유? 
IEEE에서 정의한 패킷 크기에 제한이 있음 (너무 큰 패킷은 처리를 지연시킴)
Ethernet MTU* = 1500 bytes</p>
</blockquote>
<p>*Maximum Transmission Unit
<img src="https://velog.velcdn.com/images/sua_ahn/post/f9209dba-2318-4038-9984-90a9e5e4aa89/image.png" alt=""></p>
<ol start="2">
<li>흐름 제어 
수신데이터 손실을 방지하기 위해 송신 속도와 수신측 처리 속도를 일치시킴
→ 확인 응답(ACK, NACK), 슬라이딩 윈도우</li>
</ol>
<blockquote>
<p>윈도우 크기(메모리 버퍼 여유용량)만큼 확인응답 없이 세그먼트 전송
→ 확인응답 받으면 윈도우를 옆으로 옮기고 다음 패킷 전송</p>
</blockquote>
<ol start="3">
<li>혼잡 제어 
네트워크 혼잡으로 인한 패킷 지연 및 손실을 피하기 위해 데이터 전송 속도 제어
→ 정책에 따라 혼잡 윈도우 크기 조절</li>
</ol>
<blockquote>
<p>네트워크 혼잡 
: 네트워크 내에 패킷의 수가 과도하게 증가하는 현상
→ 만약 한 라우터에 데이터가 몰릴 경우, 데이터를 모두 처리할 수 없게 되고, 호스트들은 재전송을 하게 되어 결국 혼잡만 가중</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/4fa74de4-66be-41c3-b1a6-b9a4d7ac202b/image.png" alt=""></p>
<hr>
<p><img src="https://velog.velcdn.com/images/sua_ahn/post/f1d8741b-f0fb-4392-9dbe-95478eae3bee/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>