<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kkongdo.log</title>
        <link>https://velog.io/</link>
        <description>티스토리로 기술 블로그 이전</description>
        <lastBuildDate>Sun, 13 Oct 2024 10:11:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. kkongdo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kkong_do" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[네트워크 - HTTP]]></title>
            <link>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-HTTP</link>
            <guid>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-HTTP</guid>
            <pubDate>Sun, 13 Oct 2024 10:11:32 GMT</pubDate>
            <description><![CDATA[<p><strong>HTTP</strong>는 애플리케이션 계층으로서 웹 서비스 통신에 사용된다.
HTTP/1.0부터 HTTP/3까지 발전을 거듭하였다.</p>
<h1 id="1-http10">1. HTTP/1.0</h1>
<blockquote>
</blockquote>
<p><strong>HTTP/1.0</strong>는 기본적으로 한 연결당 하나의 요청을 처리하도록 설계되어있다.</p>
<ul>
<li>한 연결당 하나의 요청을 처리하도록 연결이 되어있어 RTT 증가를 불러오게 되었다.</li>
<li>다시 말해 서버로부터 파일을 가져올 때마다 <code>TCP의 Three-Handshaking</code>를 계속해서 열어야 하기 때문에 RTT가 증가하는 단점이 있다.</li>
</ul>
<p>📢 <strong>RTT</strong>란 패킷이 목적지에 도달하고 나서 다시 출발지로 돌아오기까지 걸리는 시간, 패킷 왕복 시간</p>
<h2 id="1-1-rtt의-증가를-해결하기-위한-방법">1-1. RTT의 증가를 해결하기 위한 방법</h2>
<p>앞서 말한대로 매번 연결할 때마다 RTT가 증가하니 서버에 부담이 많이 가고 사용자 응답 시간이 길어졌다. 이를 해결하기 위해 <code>이미지 스플리팅</code>, <code>코드 압축</code>, <code>이미지 Base64 인코딩</code>을 사용한다.</p>
<h3 id="이미지-스플리팅">이미지 스플리팅</h3>
<blockquote>
</blockquote>
<p>많은 이미지를 다운로드받게 되면 과부하가 걸리기 때문에 많은 이미지가 합쳐 있는 하나의 이미지를 다운로드 받고, 이를 기반으로 <strong>background-image의 position을 이용하여</strong> 이미지를 표기하는 방법</p>
<h3 id="코드-압축">코드 압축</h3>
<blockquote>
</blockquote>
<p><strong>코드 압축</strong>은 코드를 압축해서 개행 문자, 빈칸을 없애서 코드의크기를 최소화하는 방법 </p>
<h3 id="이미지-base64-인코딩">이미지 Base64 인코딩</h3>
<blockquote>
</blockquote>
<p>이미지 파일을 64진법으로 이루어진 문자열로 인코딩하는 방법</p>
<ul>
<li>서버와의 연결을 열고 이미지에 대해 서버에 HTTP 요청을 할 필요가 없다는 장점이 있다.</li>
<li>Base64 문자열로 변환할 경우 37%정도 크기가 더 커지는 단점이 있다.</li>
</ul>
<p>📢 <strong>인코딩</strong>이란 정보의 형태나 형식을 표준화, 보안, 처리 속도 향상, 저장 공간 절약 등을 위해 다른 형태나 형식으로 변환하는 처리 방식</p>
<hr>
<h1 id="2-http11">2. HTTP/1.1</h1>
<p>HTTP/1.0에서 발전한 것이 바로 HTTP/1.1이다.</p>
<blockquote>
</blockquote>
<p>매번 TCP 연결을 하는 것이 아니라 한 번 TCP 초기화를 한 이후에 <code>keep-alive</code>라는 옵션으로 여러 개의 파일을 송신할 수 있게 바뀌었다.</p>
<p>HTTP/1.0에서도 <code>keep-alive</code>가 있었지만 표준화가 되어 있지 않고, HTTP/1.1부터 표준화가 되어 기본 옵션으로 설정되었다.</p>
<p>한번 TCP 3-Way-Handshaking이 발생하면 그다음부터 발생하지 않는다.
하지만 문서 안에 포함된 다수의 리소스(이미지, 동영상, CSS 파일, js 파일 등)를 처리하려면 요청할 리소스 개수에 비례해서 대기 시간이 길어지는 단점이 있다.</p>
<p>성능 저하 현상으로 <code>HOL Blocking</code>과 <code>무거운 헤더 구조</code>가 있다.</p>
<h2 id="2-1-hol-blocking">2-1. HOL Blocking</h2>
<blockquote>
</blockquote>
<p><strong>HOL Blocking(Head Of Line Blocking)</strong>은 네트워크에서 같은 큐에 있는 패킷이 그 첫 번째 패킷에 의해 지연될 때 발생하는 성능 저하 현상</p>
<ul>
<li>처음 파일이 느리게 받아진다면 그 뒤에 있는 것들이 대기하게 되어 다운로드가 지연되는 상태를 의미한다.</li>
</ul>
<h2 id="2-2-무거운-헤더-구조">2-2. 무거운 헤더 구조</h2>
<p>HTTP/1.1의 헤더에는 쿠키 등 많은 메타 테이터가 들어 있고 압축이 되지 않아 무겁다.</p>
<hr>
<h1 id="3-http2">3. HTTP/2</h1>
<blockquote>
</blockquote>
<p><strong>HTTP/2</strong>는 SPDY 프로토콜에서 파생된 HTTP/1.x보다 지연 시간을 줄이고 응답 시간을 더 빠르게 할 수 있으며 <code>멀티플렉싱</code>, <code>헤더 압축</code>, <code>서버 푸시</code>, <code>요청의 우선순위 처리</code>를 지원하는 프로토콜</p>
<h2 id="3-1-멀티플렉싱">3-1. 멀티플렉싱</h2>
<blockquote>
</blockquote>
<p><strong>멀티플렉싱</strong>이란 여러 개의 스트림을 사용하여 송수신하는 방법</p>
<ul>
<li>특정 스트림의 패킷이 손실되었다고 하더라도 해당 스트림에서만 영향을 미치고 나머지 스트림은 멀쩡하게 동작할 수 있다.</li>
</ul>
<p>📢 <strong>스트림</strong>이란 시간이 지남에 따라 사용할 수 있게 되는 일련의 데이터 요소를 가리키는 데이터 흐름</p>
<h2 id="3-2-헤더-압축">3-2. 헤더 압축</h2>
<p>허프만 코딩 압축 알고리즘을 사용하여 헤더의 크기를 줄인다. HPACK 압축 형식을 가지게 된다.</p>
<blockquote>
<p>📢 <strong>허프만 코딩</strong>이란 문자열을 문자 단위로 쪼개 빈도수를 세어 빈도가 높은 정보는 적인 비트 수를 사용하여 표현하고, 빈도가 낮은 정보는 비트 수를 많이 사용하여 표현함으로써 전체 데이터의 표현에 필요한 비트양을 줄이는 원리</p>
</blockquote>
<h2 id="3-3-서버-푸시">3-3. 서버 푸시</h2>
<p>클라이언트 요청 없이 서버가 바로 리소스를 푸시할 수 있다. HTTP/1.1에서는 클라리언트가 서버에 요청을 해야 파일을 다운로드 받을 수 있었다.</p>
<p>HTML과 CSS로 예를 들다자면 서버 푸시를 하지 않았을 때는 html 요청하면 html를 보내고 그다음 css를 요청하여 css를 받았는데 서버 푸시를 하게 된다면 html 파일을 요청하였을 때 css 파일도 서버에서 푸시하여 클라이언트에 먼저 줄 수 있게 되었다.</p>
<hr>
<h1 id="4-https">4. HTTPS</h1>
<p>HTTPS/2는 HTTPS 위에서 동작하게 된다.</p>
<blockquote>
</blockquote>
<p><strong>HTTPS</strong>는 애플리케이션 계층과 전송 계층 사이에 신뢰 계층인 <code>SSL/TSS 계층</code>을 넣어 신뢰할 수 있는 HTTP 요청</p>
<ul>
<li>&quot;<strong>통신을 암호화</strong>한다.&quot;</li>
</ul>
<h2 id="4-1-ssltss">4-1. SSL/TSS</h2>
<blockquote>
</blockquote>
<p><strong>SSL/TSS</strong>는 클라이언트와 서버가 통신할 때 제 3자가 메시지를 도청하거나 변조하지 못하도록 보안을 제공하는 전송 계층 프로토콜</p>
<p><strong>SSL(Secure Socket Layer)</strong>은 SSL 1.0부터시작해서 SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.3 까지 버전이 올라가며 마지막으로 TLS로 명칭이 변경되었으나 SSL/TLS로 합쳐 부른다.</p>
<p>SSL/TLS를 통해 공격자가 서버인 척 하며 사용자 정보를 가로채는 네트워크상의 &#39;인터셉터`를 방지할 수 있다.</p>
<p>SSL/TLS는 보안 세션을 기반으로 데이터를 암호화하며 보안 세션이 만들어질 때 <code>인증 매커니즘</code>, <code>키 교환 암호화 알고리즘</code>, <code>해싱 알고리즘</code>이 사용된다.</p>
<h3 id="보안-세션">보안 세션</h3>
<blockquote>
</blockquote>
<p><strong>보안 세션</strong>은 보안이 시작되고 끝나는 동안 유지되는 세션</p>
<p>SSL/TLS는 핸드쉐이크를 통해 보안 세션을 생성하고 이를 기반으로 상태 정보 등을 공유한다.</p>
<p>📢 <strong>세션</strong>이란 운영체제가 어떠한 사용자로부터 자신의 자산 이용을 허락하는 일정한 기간을 의미한다. 즉 사용자는 일정 시간동안 응용프로그램, 지원등을 사용할 수 있다.</p>
<p>클라이언트에서 <code>사이퍼 슈트(Cypher Suites)</code>를 서버에 전달하면 서버는 받은 사이퍼 슈트의 암호화 알고리즘 리스트를 제공할 수 있는지 확인한다. 제공할 수 있다면 서버에서 클라이언트로 인증서를 보내는 인증 매커니즘이 시작되고 이후 해싱 알고리즘 등으로 암호화된 데이터의 송수신이 시작된다. </p>
<h4 id="사이퍼-슈트cyper-suites">사이퍼 슈트(Cyper Suites)</h4>
<blockquote>
</blockquote>
<p>프로토콜, AEAD 사이퍼 모드, 해싱 알고리즘이 나열된 규약</p>
<p><strong>AEAD 사이퍼 모드(Authenticated Encrption with Associated Data)</strong>: 데이터 암호화 알고리즘</p>
<h3 id="인증-매커니즘">인증 매커니즘</h3>
<blockquote>
</blockquote>
<p><strong>CA(Certificate Authorities)</strong>에서 발급한 인증서를 기반으로 이루어지는 매커니즘</p>
<p>CA에서 발급한 인중서는 안전한 연결을 시작하는 데 있어 필요한 &#39;공개키&#39;를 클라이언트에 제공하고 사용자가 접속한 &#39;서버가 신뢰&#39;할 수 있는 서버임을 보장한다. 인증서는 서비스 정보, 공개키, 지문, 디지털 서명 등으로 이루어져 있다.</p>
<p>CA는 신뢰성이 엄격하게 공인된 기업들만 참여할 수 있으며, 대표적인 기업으로는 Comodo, GoDaddy, GlobalSign, 아마존 등이 있다.</p>
<h4 id="ca-발급-과정">CA 발급 과정</h4>
<p>자신의 서비스가 CA 인증서를 발급받으려면 자신의 사이트 정보와 공개키를 CA에 제출하여야 한다. 이후 CA는 공개키를 해시한 값인 지문(finger Print)을 사용하는 CA의 비밀키 등을 기반으로 CA 인증서를 발급한다.</p>
<h3 id="암호화-알고리즘">암호화 알고리즘</h3>
<blockquote>
</blockquote>
<p>키 교환 암호화 알고리즘으로는 대수곡선 기반의 ECDHE 또는 모듈식 기반읜 DHE를 사용한다. 둘 다 디피-헬만 방식을 근간으로 만들어졌다.</p>
<h4 id="디피-헬만-키-교환-암호화-알고리즘">디피-헬만 키 교환 암호화 알고리즘</h4>
<blockquote>
</blockquote>
<p><strong>디피-헬만 키 교환(Diffie-Hellman key exchange) 암호화 알고리즘</strong>은 암호키를 교환하는 하나의 방법</p>
<h3 id="해싱-알고리즘">해싱 알고리즘</h3>
<blockquote>
</blockquote>
<p><strong>해싱 알고리즘</strong>은 데이터를 추정하기 힘든 더 작고, 섞여 있는 조각으로 만드는 알고리즘</p>
<ul>
<li>종류로는 <code>SHA-256 알고리즘</code>, <code>SHA-284 알고리즘</code>이 있다.</li>
</ul>
<h4 id="sha-256-알고리즘">SHA-256 알고리즘</h4>
<blockquote>
</blockquote>
<p><strong>SHA-256 알고리즘</strong>은 해시 함수의 결괏값이 256비트인 알고리즘</p>
<ul>
<li>해싱을 해야 할 메세지에 1을 추가하는 등 전처리를 하고 전처리된 메세지를 기반으로 해시를 반환한다. </li>
</ul>
<blockquote>
<p><strong>해시</strong>, <strong>해싱</strong>, <strong>해시 함수</strong>는 무엇인가?🤔</p>
</blockquote>
<ul>
<li><strong>해시</strong>는 다양한 길이를 가진 데이터를 고정된 길이를 가진데이터로 매핑한 값 </li>
<li><strong>해싱</strong>은 임의의 데이터를 해시로 바꿔주는 일이며 해시 함수가 이를 담당</li>
<li><strong>해시 함수</strong>는 임의의 데이터를 입력으로 받아 일정한 길이의 데이터로 바꿔주는 함수</li>
</ul>
<h2 id="4-2-seo에도-도움이-되는-https">4-2. SEO에도 도움이 되는 HTTPS</h2>
<p>구글은 SSL 인증서를 강조해왔고 사이트 내 모든 요소가 동일하다면 HTTPS 서비스를 하는 사이트가 그렇지 않은 사이트보다 SEO순위가 높다고 공식으로 밝혔다.</p>
<h3 id="seo">SEO</h3>
<blockquote>
</blockquote>
<p><strong>SEO(Search Engine Optimization)</strong>는 검색엔진 최적화를 뜻하며, 사용자들이 구글, 네이버 같은 검색엔진으로 웹 사이트를 검색했을 때 그 결과를 페이지 상단에 노출시켜 많은 사람이 볼 수 있도록 최적화하는 방법</p>
<ul>
<li>SEO를 관리하는 방법에는 <code>캐노니컬 설정</code>, <code>메타 설정</code>, <code>페이지 속도 개선</code>, <code>사이트맵 관리</code> 등이 있다.</li>
</ul>
<h2 id="4-3-https-구축-방법">4-3. HTTPS 구축 방법</h2>
<p>HTTPS를 구축할 수 있는 방법에는 <code>1. 직접 CA에서 구매한 인증키를 기반으로 HTTPS 서비스를 구축하거나</code>, <code>2. 서버 앞단의 HTTPS를 제공하는 로드 밸런서를 두거나</code>, <code>서버 앞단에 HTTPS를 제공하는 CDN을 둬서 구축</code>한다.</p>
<hr>
<h1 id="5-http3">5. HTTP/3</h1>
<p>HTTP/3은 HTTP/1.1 및 HTTP/2와 함께 World Wide Web에서 정보를 교환하는데 사용되는 HTTP의 세 번째 버전이다.</p>
<p>TCP 위에서 돌아가는 HTTP/2와는 달리 HTTP/3은 QUIC이라는 계층 위에서 돌아가며, <strong>TCP 기반이 아닌 UDP 기반</strong>으로 돌아간다.</p>
<p>HTTP/2에서 장점이었던 <strong>멀티플렉싱을 가지고 있으며 초기 연결 설정 시 지연시간 감소라는 장점</strong>이 있다. </p>
<h2 id="초기-연결-설정-시-지연-시간-감소">초기 연결 설정 시 지연 시간 감소</h2>
<p>QUIC은 TCP를 사용하지 않기 때문에 통신을 시작할 때 번거로운 3-way-handshaking 과정을 거치지 않아도 된다.</p>
<p>QUIC는 첫 연결 설정에 1-RTT만 소요되고 클라이언트가 서버에 어떤 신호를 한 번 주고, 서버도 거기에 응답하기만 하면 바로 본 통신을 시작할 수 있다.</p>
<p>QUIC는 순방향 오류 수정 매커니즘(FEC, Forward Error Correction)이 적용되어 있다. 이는 전송한 패킷이 손실되어있다면 수신 측에서 에러를 검출하고 수정하는 방식이며 열악한 네트워크 환경에서도 낮은 패킷 손실률을 자랑한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 - IP 주소]]></title>
            <link>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-IP-%EC%A3%BC%EC%86%8C</link>
            <guid>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-IP-%EC%A3%BC%EC%86%8C</guid>
            <pubDate>Sat, 12 Oct 2024 09:46:33 GMT</pubDate>
            <description><![CDATA[<p>컴퓨터와 컴퓨터 간의 통신은 IP 주소 기반으로 통신한다고 하지만 정확히 이야기하면 <code>IP 주소에서 ARP를 통해 MAC 주소를 찾아 MAC 주소를 기반으로 통신</code>한다.</p>
<h1 id="1-arp">1. ARP</h1>
<blockquote>
</blockquote>
<p><strong>ARP(Address Resolution Protocol)</strong>이란 IP 주소로부터 MAC 주소를 구하는 IP와 MAC 주소의 다리 역할을 하는 프로토콜</p>
<p><code>ARP</code>를 통해 가상 주소인 IP 주소를 실제 주소인 MAC 주소로 변환한다. 반대로 <code>RARP</code>를 통해 실제 주소인 MAC 주소를 가상 주소인 IP 주소로 변환한다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/bb720949-8594-4825-a6d0-3433f728e234/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kkong_do/post/38e55766-0894-49fd-a393-70656b147784/image.jpg" alt=""></p>
<p>두번째 그림처럼 장치 A가 ARP Request 브로드캐스트를 보내서 IP 주소인 120.70.80.3에 해당하는 MAC 주소를 찾는다. 그리고 난 후 해당 주소에 맞는 장치 B가 ARP Reply 유니캐스트를 통해 MAC 수조를 반환하는 과정을 거쳐 IP 주소에 맞게 MAC 주소를 찾게 된다.</p>
<ul>
<li><code>브로트캐스트(Broadcast)</code> : 송신 호스트가 전송한 데이터가 네트워크에 연결된 모든 호스트에 전송되는 방식</li>
<li><code>유니캐스트(Unicast)</code> : 고유 주소로 식별된 하나의 네트워크 목적지에 1:1로 데이터를 전송하는 방식</li>
</ul>
<hr>
<h1 id="2-홉바이홉-통신">2. 홉바이홉 통신</h1>
<blockquote>
</blockquote>
<p>IP 주소를 통해 통신하는 과정</p>
<ul>
<li><strong>홉(Hop)</strong> : 사전적 의미로서는 건너뛰는 모습을 의미하며 통신망에서 각 패킷이 여러 개의 라우터를 건너가는 모습을 비유적으로 표현한 것</li>
<li>아래 그림처럼 수많은 서브네트워크 안에 있는 라우터의 라우팅 테이블 IP를 기반으로 패킷을 전달하고 또 전달해나가며 라우팅을 수행하며 최종 목적지까지 패킷을 전달한다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>홉바이홉 통신</strong>은 통신 장치에 있는 &#39;라우팅 테이블&#39;의 IP를 통해 시작 주소로부터 시작하여 다음 IP로 계속해서 이동하는 &#39;라우팅&#39; 과정을 거쳐 패킷이 최종 목적지까지 도달하는 통신</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/d1bce32b-2a22-4d44-9409-bce63b99e199/image.jpg" alt=""></p>
<ul>
<li><code>라우팅(Routing)</code> : IP 주소를 찾아가는 과정</li>
</ul>
<h2 id="2-1-라우팅-테이블">2-1. 라우팅 테이블</h2>
<blockquote>
</blockquote>
<p><strong>라우팅 테이블(Routing Table)</strong>은 송신지에서 수신기까지 도달하기 위해 사용되며 라우터에 들어가 있는 목적지 정보들과 그 목적지로가기 위한 방법이 들어 있는 리스트 </p>
<ul>
<li>라우팅 테이블은 게이트웨이와 모든 목적지에 대해 해당 목적지에 도달하기 위해 거쳐야 할 다음 라우터의 정보를 가진다.</li>
</ul>
<h2 id="2-2-게이트웨이">2-2. 게이트웨이</h2>
<blockquote>
</blockquote>
<p><strong>게이트웨이(Gateway)</strong>는 서로 다른 통신망, 프로토콜을 사용하는 네트워크 간의 통신을 가능하게 하는 관문 역할을 하는 컴퓨터나 소프트웨어</p>
<ul>
<li>사용자는 인터넷에 접속하기 위해 수많은 톨게이트인 게이트웨이를 거챠여 하며 게이트웨이는 서로 다른 네트워크상의 통신 프로토콜을 변환해주는 역할을 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/9700619b-8dca-48fb-bfea-df1b51410e7a/image.jpg" alt=""></p>
<hr>
<h1 id="3-ip-주소-체계">3. IP 주소 체계</h1>
<blockquote>
</blockquote>
<p>IP 주소 체계는 IPv4와 IPv6로 나뉜다.</p>
<ul>
<li><code>IPv4</code>는 32비트를 8비트 단위로 점을 찍어 표기하며, 123.45.67.89 같은 방식으로 나타낸다.</li>
<li><code>IPv6</code>는 64비트를 16비트 단위로 점을 찍어 표기하며, 2001:db8::ff00:45:8329같은 방식으로 나타낸다. </li>
</ul>
<p>현재 가장 많이 사용되는 주소 체계는 IPv4이다.</p>
<h2 id="3-1-클래스-기반-할당-방식">3-1. 클래스 기반 할당 방식</h2>
<p>A, B, C, D, E 다섯 개의 클래스르 구분하는 <code>클래스 기반 할당 방식</code>을 처음에는 사용하였다.</p>
<p>앞에 있는 부분을 <code>네트워크 주소</code>, 그 뒤에 있는 부분을 컴퓨터에 부여하는 주소인 <code>호스트 주소</code>로 놓아서 사용한다.</p>
<p><code>클래스 A, B, C</code>는 일대일 통신으로 사용되고 <code>클래스 D</code>는 멀티캐스트 통신, <code>클래스 E</code>는 앞으로 사용할 예비용으로 쓰는 방식이다.</p>
<ul>
<li><code>클래스 A의 범위</code>는 0.0.0.0 ~  127.255.255.255이다.</li>
<li><code>클래스 B의 범위</code>는 128.0.0.0 ~  191.255.255.255이다.</li>
<li><code>클래스 C의 범위</code>는 192.0.0.0 ~  223.255.255.255이다.</li>
</ul>
<p>네트워크 첫 번째 주소는 네트우크 주소로 사용되고, 가장 마지막 주소는 브로드캐스트용 주소로 사용되기 때문에 이를 제외한 나머지 주소를 사용할 수 있다.</p>
<h2 id="3-2-dhcp">3-2. DHCP</h2>
<blockquote>
</blockquote>
<p><strong>DHCP(Dynamic Host Configuration Protocol)</strong>는 IP 주소 및 기타 통신 매개변수를 자동으로 할당하기 위한 네트워크 관리 프로토콜</p>
<ul>
<li>DHCP를 통해 네트워크 장치의 IP주소를 수동으로 설정할 필요 없이 인터넷에 접속할 마다 자동으로 IP 주소를 할당할 수 있다.</li>
</ul>
<h2 id="3-3-nat">3-3. NAT</h2>
<blockquote>
</blockquote>
<p><strong>NAT(Network Address Translation)</strong>는 패킷이 라우팅 장치를 통해 전송되는 동안 패킷 IP 주소 정보를 수정하여 IP 주소를 다른 주소로 매핑하는 방법</p>
<ul>
<li>IPv4 주소 체계만으로 많은 주소들을 모두 감당하지 못한 단점을 해결하기 위해서 사용되며 공인 IP와 사설 IP를 나누어서 많은 주소를 처리한다.</li>
<li>ICS, RRAS, Netfilter 등이 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/f2fc4664-52ec-4b25-8b55-4c8220ee2d17/image.jpg" alt=""></p>
<h3 id="공유기와-nat">공유기와 NAT</h3>
<blockquote>
</blockquote>
<p>NAT를 쓰는 이유는 주로 여러 대의 호스트가 하나의 공인 IP 주소를 사용하여 인터넛에 접속하기 위함이다.</p>
<ul>
<li>인터넷 회선 하나를 개통하고 인터넷 공유기를 달아서 여러 PC를연결하여 사용할 수 있는데 이것이 가능한 이유가 바로 인터넷 공유기에 NAT 기능이 탑재되어 있기 때문이다.</li>
</ul>
<h3 id="nat를-이용한-보안">NAT를 이용한 보안</h3>
<blockquote>
</blockquote>
<p>NAT를 이용하면 내부 네트워크에서 사용하는 IP 주소와 외부에 드러나는 IP 주소를 다르게 유지할 수 있기 때문에 내부 네트워크에 대한 어느 정도의 보안이 가능하다.</p>
<h3 id="nat의-단점">NAT의 단점</h3>
<blockquote>
</blockquote>
<p>NAT는 여려 명이 동시에 인터넷을 접속하게 되므로 실제로 접속하는 호스트 숫자에 따라서 접속 속도가 느려질 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 - 네트워크 기기]]></title>
            <link>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EA%B8%B0</link>
            <guid>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EA%B8%B0</guid>
            <pubDate>Sun, 06 Oct 2024 05:30:15 GMT</pubDate>
            <description><![CDATA[<h1 id="1-네트워크-기기의-처리-범위">1. 네트워크 기기의 처리 범위</h1>
<p><strong>네트워크 기기</strong>는 계층별로 처리 범위를 나눌 수 있다. 물리 계층을 처리할 수 있는 기기와 데이터 링크 계층을 처리할 수 있는 기기 등이 존재한다. 또한, 상위 계층을 처리하는 기기는 하위 계층을 처리할 수 있지만 반대로는 불가능하다!</p>
<h3 id="계층별-네트워크-기기📡">계층별 네트워크 기기📡</h3>
<ul>
<li>애플리케이션 계층 : L7 스위치</li>
<li>인터넷 계층 : L3 스위치</li>
<li>데이터 링크 계층 : L2 스위치, 브리지</li>
<li>물리 계층 : NIC, 리피터, AP</li>
</ul>
<blockquote>
<p><strong>스위치</strong>는 여러 장비를 연결하고 데이터 통신을 중재하며 목적지가 연결된 포트로만 전기 신호를 보내 데이터를 전송하는 통신 네트워크 장비</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/586a0a33-e529-42ad-b2a5-7bd45be28532/image.jpg" alt=""></p>
<h1 id="2-애플리케이션-계층을-처리하는-기기">2. 애플리케이션 계층을 처리하는 기기</h1>
<h2 id="2-1-l7-스위치">2-1. L7 스위치</h2>
<blockquote>
</blockquote>
<p><strong>L7 스위치</strong>는 로드밸런서라고 하며, 서버의 부하를 분산하는 기기</p>
<ul>
<li>클라이언트로부터 오는 요청들을 뒤쪽의 여러 서버로 나누는 역할을 수행한다.</li>
<li>시스템이 처리할 수 있는 트래픽 증가를 목표로 한다.</li>
<li>URL, 서버, 캐시, 쿠키들을 기반으로 트래픽을 분산한다.</li>
<li>바이러스, 불필요한 외부 데이터 등을 걸러내는 필터링 기능을 가지고 있다.</li>
<li>응용 프로그램 수준의 트래픽 모니터링이 가능하다.</li>
</ul>
<p>장애가 발생한 서버가 있다면 이를 트래픽 분산 대상에서 제외해야 하는데 정기적으로 헬스 체크(Health Check)을 이용하여 감시하면서 이루어진다.</p>
<h3 id="l4-스위치와-l7-스위치-차이점은🤔">L4 스위치와 L7 스위치 차이점은?🤔</h3>
<p><strong>L4 스위치</strong>는 전송 계층을 처리하는 기기로 스트리밍 관련 서비스에서는 사용할 수 없다. 또한, 메시지를 기반으로 인식하지 못하고 IP와 포트를 기반으로 트래픽을 분산한다. (특히 포트를 기반으로 한다.)</p>
<p><strong>L7 스위치</strong>는 IP, 포트 외에도 URL, HTTP 헤더, 쿠키 등을 기반으로 트래픽을 분산한다.</p>
<p>_<u>L7 스위치를 이용한 로드밸런싱</u>_을 <strong>ALB(Application Load Balancer) 컴포넌트</strong>라고 하며, _<u>L4 스위치를 이용한 로드밸런싱</u>_을 <strong>NLB(Network Load Balancer) 컴포넌트</strong>라고 한다.</p>
<h3 id="헬스-체크health-check">헬스 체크(Health Check)</h3>
<blockquote>
</blockquote>
<p>전송 주기와 재전송 횟수 등을 설정한 이후 반복적으로 서버에 요청을 보내는 것 </p>
<p>L4 스위치 또는 L7 스위치는 헬스 체크를 통해 정상적인 서버와 비정상적인 서버를 판별한다.</p>
<p>서버에 부하가 되지 않을 만큼 요청 횟수가 적절하게 이루어져야 하며 TCP, HTTP 등 다양한 방법으로 요청을 보내며 이 요청이 정상적으로 이루어졌다면 정상적인 서버로 판별한다.</p>
<h3 id="로드밸런서를-이용한-서버-이중화">로드밸런서를 이용한 서버 이중화</h3>
<p>로드밸런서는 대표적으로 서버 이중화의 기능으로 사용한다. 만약 에러가 발생하여 서버 1대가 종료되더라도 서비스가 안정적으로 운용하도록 2대 이상의 서버를 필수적으로 둘 때 사용한다.</p>
<p>로드밸런서는 2대 시앙의 서버를 기반으로 가상 IP를 제공하고 이를 기반으로 안정적인 서비스를 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/5fd1bc20-8b65-4b17-b4db-f0e535ae90aa/image.jpg" alt=""></p>
<h1 id="3-인터넷-계층을-처리하는-기기">3. 인터넷 계층을 처리하는 기기</h1>
<h2 id="3-1-라우터">3-1. 라우터</h2>
<blockquote>
</blockquote>
<p>다른 네트워크에 존재하는 장치끼리 서로 데이터를 주고받을 때 패킷 소모를 최소화하고 경로를 최적화하여 최소 경로로 패킷을 포워딩하는 <em>&#39;라우팅&#39;</em>을 수행하는 장비</p>
<ul>
<li>여러 개의 네트워크를 연결, 분할, 구분시켜주는 역할을 한다.</li>
</ul>
<p>📢<strong>라우터</strong>는 소프트웨어 기반의 라우팅을 하는 장치과 하드웨어 기반의 라우팅을 하는 장치로 구분된다.</p>
<h2 id="3-2-l3-스위치">3-2. L3 스위치</h2>
<blockquote>
</blockquote>
<p>L2 스위치의 기능 + 라우팅 기능을 갖춘 장비</p>
<ul>
<li>L3 스위치 = &#39;라우터&#39; 라고 한다.</li>
<li>하드웨어 기반의 라우팅을 담당하는 장치이다.</li>
</ul>
<h1 id="4-데이터-링크-계층을-처리하는-기기">4. 데이터 링크 계층을 처리하는 기기</h1>
<h2 id="4-1-l2-스위치">4-1. L2 스위치</h2>
<blockquote>
</blockquote>
<p>장치들의 MAC 주소를 MAC 주소 테이블을 통해 관리하며 연결된 장치로 부터 패킷이 왔을 때 패킷 전송을 담당</p>
<ul>
<li>IP 주소를 이해하지 못하기 때문에 IP 주소를 기반으로 라우팅이 불가능하다.</li>
<li>패킷의 MAC 주소를 읽어 스위칭하는 역할을 한다.</li>
<li>목적지가 MAC 주소 테이블에 없다면 전체 포트에 전달하고 MAC 주소 테이블의 주소는 일정 시간 이후 삭제하는 기능이 있다.</li>
</ul>
<h2 id="4-2-브리지bridge">4-2 브리지(Bridge)</h2>
<blockquote>
</blockquote>
<p>두 개의 근거리 통신망(LAN)을 상호 접속할 수 있도록 하는 통신망 연결 장치</p>
<ul>
<li>포트와 포트 사이의 다리 역할을 하며 장치에서 받아온 MAC 주소를 MAC 주소 테이블로 관리한다.</li>
<li>통신망 범위를 확장하고 서로 다른 LAN 등으로 이루어진 &#39;하나의&#39; 통신망을 구축할 때 사용한다.</li>
</ul>
<h1 id="5-물리-계층을-처리하는-기기">5. 물리 계층을 처리하는 기기</h1>
<h2 id="5-1-nicnetwork-interface-card">5-1. NIC(Network Interface Card)</h2>
<blockquote>
</blockquote>
<p>2대 이상의 컴퓨터 네트워클르 구성하는데 사용하며 네트워크과 빠른 속도로 데이터를 송수인할 수 있도록 컴퓨터 내 설치하는 확장 카드</p>
<ul>
<li>LAN 카드라고 하는 네트워크 인터페이스 카드이다.</li>
<li>각각의 LAN 카드에는 주민등록번호처럼 각각의 구분하기 위한 고유의 식별번호(=MAC 주소)가 존재한다.</li>
</ul>
<h2 id="5-2-리피터repeater">5-2. 리피터(Repeater)</h2>
<blockquote>
</blockquote>
<p>약해진 신호 정도를 증폭하여 다른 쪽으로 전달하는 장치</p>
<ul>
<li>패킷을 더 멀리 갈 수 있게 해준다. 하지만 현대에는 광케이블이 보급됨에 따라 잘 쓰이지 않는 장치이다.</li>
</ul>
<h2 id="5-3-apaccess-point">5-3. AP(Access Point)</h2>
<blockquote>
</blockquote>
<p>패킷을 복사하는 기기</p>
<ul>
<li>유선 LAN을 연결한 후 다른 장치에서 무선 LAN 기술(와이파이 등)을 사용하여 무선 네트워크 연결을 할 수 있도록 기능을 제공하는 장치이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 - TCP/IP 4계층 모델]]></title>
            <link>https://velog.io/@kkong_do/Network-TCPIP-4%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@kkong_do/Network-TCPIP-4%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Sat, 05 Oct 2024 10:28:29 GMT</pubDate>
            <description><![CDATA[<h1 id="1-계층구조">1. 계층구조</h1>
<p><strong>인터넷 프로토콜 스위트(Internet Protocal Suite)</strong>는 인터넷에서 컴퓨터들이 서로 정보를 주고받는 데 쓰이는 프로토콜의 집합이다.
인터넷 프로토콜 스위트는 <strong>TCP/IP 4계층 모델</strong>로 설명하거나 <strong>OSI 7계층 모델</strong>로 설명한다.</p>
<blockquote>
</blockquote>
<p><strong>TCP/IP(Transmission Control Protocol/Internet Protocol) 4계층</strong>은 네트워크에서 사용되는 통신 프로토콜의 집합</p>
<ul>
<li>프로토콜의 네트워킹 범위에 따라 네 개의 추상화 계층으로 구성된다.</li>
</ul>
<blockquote>
</blockquote>
<p>** OSI 7계층**은 TCP/IP와 달리 7개의 계층으로 나뉘어 각 계층에서 이루어지는 네트워크 통신을 보다 세분화한 네트워크 통신 프로토콜의 집합</p>
<ul>
<li>OSI 게층은 애플리케이션 계층을 새 개로 쪼개고, 링크 계층을 데이터 링크 계층, 물리 계층으로 나눠서 표현하며 인터넷 계층을 네트워크 계층으로 부른다는 점이 TCP/IP와 다르다.
<img src="https://velog.velcdn.com/images/kkong_do/post/89bf916c-a505-4bc3-8a6a-8daef005c429/image.jpg" alt=""></li>
</ul>
<p>TCP/IP와 OSI 모델은 네트워크 통신을 설명하는 두 가지 방식으로, TCP/IP는 실제 구현을 중심으로 만들어진 반면, OSI는 네트워크 통신의 개념적 모델을 제공하며 이론적인 구조를 설명한다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/27689ee8-9899-4507-93da-f9cb76109e43/image.jpg" alt=""></p>
<p>이러한 계층들은 특정 계층이 변경되었을 때 다른 계층이 영향을 받지않도록 설계가 되어있다.</p>
<hr>
<h2 id="1-1-애플리케이션-계층">1-1. 애플리케이션 계층</h2>
<blockquote>
</blockquote>
<p><strong>애플리케이션(Application)계층</strong>은 FTP, HTTP, SSH, SMTP, DNS 등 응용 프로그램이 사용되는 프로토콜 계층</p>
<ul>
<li>웹 서비스, 이메일 등 서비스를 실질적으로 사람들에게 제공하는 계층이다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>📢 FTP, HTTP, SSH, SMTP, DNS란 무엇인가??🤔</strong></p>
<ul>
<li><strong>FTP</strong> : 파일을 서버와 클라이언트 간에 전송하는 데 사용되는 표준 네트워크 프로토콜</li>
<li><strong>HTTP</strong> : World Wide Web을 위한 데이터 통신의 기초이자 웹 사이트를 이용하는데 사용되는 프로토콜, 웹 브라우저와 웹 서버 간에 데이터를 주고받기 위한 프로토콜</li>
<li><strong>SSH</strong> : 보안되지 않은 네트워크에서 네트워크 서비스를 안전하게 운영하기 위한 암호화 네트워크 프로토콜</li>
<li><strong>SMTP</strong> : 전자 메일 전송을 위한 인터넷 표준 통신프 프로토콜</li>
<li><strong>DNS</strong> : 도메인 이름과 IP 주소를 매팽해주는 서버</li>
</ul>
<hr>
<h2 id="1-2-전송-계층">1-2. 전송 계층</h2>
<blockquote>
</blockquote>
<p><strong>전송(Transport) 계층</strong>은 네트워크 통신에서 데이터를 신뢰성 있게 송수신하도록 지원하는 역할을 하는 계층</p>
<ul>
<li>송신자와 수신자를 연결하는 통신 서비스를 제공하며 연결 지향 데이터 스트림 지원, 신뢰성, 흐름 제어를 제공한다.</li>
<li>연결 지향 데이터 스트림 지원, 신뢰성, 흐름 제어를 제공할 수 있으며 애플리케이션과 인터넷 계층 사이의 데이터가 전달될 때 중계 역할을 한다. </li>
<li>대표적으로 TCP와 UDP가 있다.</li>
</ul>
<p><strong>TCP</strong>는 <strong><em>패킷 사이의 순서를 보장</em></strong>하고 <strong><em>연결지향 프로토콜을 사용</em></strong>해서 연결한다. 또한, <strong><em>신뢰성을 구축</em></strong>해서 <strong><em>수신 여부를 확인</em></strong>하며 <strong><em>&#39;가상회선 패킷 교환 방식&#39;</em></strong>을 사용한다.
<strong>UDP</strong>는 <strong><em>패킷 사이의 순서를 보장하지 않고</em></strong> <strong><em>수신 여부를 확인하지 않으며</em></strong> 단순히 데이터만 주는 <strong><em>&#39;데이터그램 패킷 교환 방식`</em></strong>을 사용한다. </p>
<h3 id="가상회선-패킷-교환-방식">가상회선 패킷 교환 방식</h3>
<blockquote>
</blockquote>
<p><strong>가상회선 패킷 교환 방식</strong>은 각 패킷에는 가상회선 식별자가 포함되며 모든 패킷을 전송하면 가상회선이 해제되고 패킷들은 전송된 <strong><em>&#39;순서대로&#39;</em></strong> 도착하는 방식</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/cac57de4-70fc-4597-a730-59abb5e35b37/image.jpg" alt=""></p>
<h3 id="데이터그램-패킷-교환-방식">데이터그램 패킷 교환 방식</h3>
<blockquote>
</blockquote>
<p><strong>데이터그램 패킷 교환 방식</strong>은 패킷이 독립적으로 이동하며 최적의 경로를 선택하여 가고 하나의 메시지에서 분할된 여러 패킷은 서로 다른 경로로 전송될 수 있으며 도착한 <strong><em>&#39;순서가 다를 수 있는&#39;</em></strong> 방식</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/65b817da-050c-4b47-9913-4d1d441fe3c2/image.jpg" alt=""></p>
<h3 id="tcp-연결-성립-과정">TCP 연결 성립 과정</h3>
<p>TCP는 신뢰성을 확보할 때 <code>3-Way-Handshake</code>라는 작업을 진행한다.
다음 그림과 같이 클라이언트와 서버가 통신할 때 세 단계의 과정을 거친다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/b661065f-1b81-42ff-8865-df33ff6a1e34/image.jpg" alt=""></p>
<h4 id="세단계의-과정-설명">세단계의 과정 설명</h4>
<ol>
<li><p><code>SYN</code> 단계 : 클라이언트는 서버에 클라이언트의 ISN을 담아 SYN을 보낸다. ISN은 새로운 TCP 연결의 첫 번째 패킷에 할당된 임의의 시퀀스 번호를 의미하며 이는 장치마다 다르다.</p>
</li>
<li><p><code>SYN + ACK</code> 단계 : 서버는 클라이언트의 <code>SYN</code>을 수신하고 서버의 <code>ISN</code>을 보내며 승인번호로 클라이언트의 <code>ISN + 1</code>을 보낸다.</p>
</li>
<li><p><code>ACK</code> 단계 : 클라이언트는 서버의 <code>ISN + 1</code>한 값인 승인번호를 담아 <code>ACK</code>를 서버로 보낸다.</p>
</li>
</ol>
<p><code>3-Way-Handshake</code> 과정 이후 신뢰성이 구축되고 데이터 전송을 시작한다. TCP는 이 과정이 있기 때문에 신뢰성이 보장되는 계층이라고 불리며, UDP는 이 과정이 없기 때문에 신뢰성이 보장되지 않는 계층이라고 한다.</p>
<blockquote>
<p>📢 <strong>SYN, ACK, ISN</strong>이란 무엇인가?🤔</p>
</blockquote>
<ul>
<li>SYN : 연결 요청 플래그</li>
<li>ACK : 응답 플래그</li>
<li>ISN : 초기 네트워크 연결을 할 때 할당되는 32비트 고유 시퀀스 번호</li>
</ul>
<h3 id="tcp-연결-해제-과정">TCP 연결 해제 과정</h3>
<p>TCP 연결을 해제할 때는 <code>4-Way-Handshake</code> 과정이 발생한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/67db24d1-465b-4ac5-bcb7-9ace00dd9eba/image.jpg" alt=""></p>
<h4 id="단계의-과정-설명">단계의 과정 설명</h4>
<p><strong>순서 1</strong>: 먼저 클라이언트가 연결을 닫을려고 할 때 <code>FIN</code>으로 설정된 세그먼트를 보낸다. 클라이언트는 <code>FIN_WAIT_1</code> 상태로 들어가고 서버의 응답을 기다린다.
<strong>순서 2</strong> : 서버는 클라이언트로 <code>ACK</code>라는 승인 세그먼트를 보낸다. 그리고 <code>CLOSE_WAIT</code> 상태에 들어간다. 클라이언트가 세그먼트를 받으면 <code>FIN_WAIT_2</code> 상태에 들어간다.
<strong>순서 3</strong> : 서버는 <code>ACK</code>를 보내고 일정 시간 이후에 클라리언트에 <code>FIN</code>이라는 세그먼트를 보낸다.
<strong>순서 4</strong> : 클라이언트는 <code>TIME_WAIT</code> 상태가 되고 다시 서버로 <code>ACK</code>를 보내서서버는 <code>CLOSED</code> 상태가 된다. 이후 클라이언트는 어느 정도의 시간을 대기한 후 연결이 닫히고 클라이언트와 서버의모든 자원의 연결이 해제된다. </p>
<blockquote>
<p><strong>TIEM_WAIT를 두는 이유?🤔</strong>
첫 번째, 지연 패킷이 발생하는 경우를 대비하기 위해서 둔다.
두 번째, 두 장치가 연결이 닫혔는지 확인하기 위해서 둔다.</p>
</blockquote>
<p><strong>📢  TIME_WAIT란?🤔</strong>
소켓이 바로 소멸되지 않고 일정 시간 유지되는 상태를 말하며 지연 패킷 등의 문제점을 해결하는데 사용된다.</p>
<hr>
<h2 id="1-3-인터넷-계층">1-3. 인터넷 계층</h2>
<blockquote>
</blockquote>
<p><strong>인터넷(Internet) 계층</strong>은 장치로부터 받은 네트워크 패킷을 IP 주소로 지정된 목적지로 전송하기 위해 사용되는 계층</p>
<ul>
<li>패킷을 수신해야 할 상대의 주소를 지정하여 데이터를 전달한다.</li>
<li>IP, ARP, ICMP 등이 존재한다.</li>
<li>상대방이 제대로 받았는지에 대해 보장하지 않는 비연결형적인 특징을 가지고 있다.</li>
</ul>
<p><strong>📢 IP, ARP, ICMP란 무엇인가??🤔</strong></p>
<p><strong>IP (Internet Protocol)</strong> : 네트워크 상에서 데이터를 전송하기 위해 사용되는 주요 프로토콜</p>
<ul>
<li>데이터를 패킷으로 분할하여 송신자와 수신자 간에 전달하는 역할을 한다.</li>
<li>주로 라우팅을 담당하며, 송신자의 IP 주소와 수신자의 IP 주소를 기반으로 데이터를 목적지까지 전달한다.</li>
</ul>
<p><strong>ARP (Address Resolution Protocol)</strong> : IP 주소를 물리적인 MAC 주소로 변환하는 프로토콜 </p>
<ul>
<li>IP 주소만으로는 같은 네트워크 내의 장치와 직접 통신할 수 없기 때문에 ARP는 IP 주소에 해당하는 물리적인 네트워크 인터페이스(네트워크 카드)의 MAC 주소를 찾아낸다.</li>
</ul>
<p><strong>ICMP (Internet Control Message Protocol)</strong> : 네트워크 장치들 간의 오류 메시지 전달과 네트워크 상태를 모니터링하는 데 사용되는 프로토콜</p>
<ul>
<li>데이터 전송 중에 문제가 발생했을 때, ICMP는 해당 문제를 송신자에게 알려 네트워크 오류를 처리하거나 진단할 수 있도록 한다.</li>
</ul>
<hr>
<h2 id="1-4-링크-계층네트워크-계층">1-4. 링크 계층(=네트워크 계층)</h2>
<blockquote>
</blockquote>
<p><strong>링크(Link) 계층</strong>은 전선, 광섬유, 무선 등으로 실질적으로 데이터를 전달하며 장치 간에 신호를 주고받는 <code>규칙</code>을 정하는 계층</p>
<ul>
<li>링크 계층은 OSI 7계층에서는 물리 계층과 데이터 링크 계층으로 나뉜다.</li>
<li>물리 계층은 무선 LAN과 유선 LAN을 통해 0과 1로 이루어진 데이터를 보내는 계층을 의미한다. </li>
<li>데이터 링크 계층은 &#39;이더넷 프레임&#39;을 통해 에러 확인, 흐름 제어, 접근 제어를 담당하는 계층을 의미한다.</li>
</ul>
<h3 id="유선-lanieee8023">유선 LAN(IEEE802.3)</h3>
<p>유선 LAN을 이루는 이더넷은 IEEE802.3이라는 프로토콜을 따르며 <code>전이중화통신</code>, <code>CSMA/CD</code>을 사용한다. </p>
<h4 id="전이중화-통신full-duplex">전이중화 통신(Full Duplex)</h4>
<blockquote>
</blockquote>
<p><strong>전이중화(Full Duplex) 통신</strong>은 양쪽 장치가 동시에 송수신할 수 있는 방식</p>
<ul>
<li>송신로와 수신로로 나눠서 데이터를 주고받으며 현대의 고속 이더넷은 빙식을 기반으로 통신하고 있다.</li>
</ul>
<h4 id="csmacd">CSMA/CD</h4>
<blockquote>
</blockquote>
<p><strong>CSMA/CD(Carrier Sense Multiple Access with Collision Detection)</strong>는 데이터를 <code>보낸 이후</code> 충돌이 발생한다면 일정 시간 이후 재전송하는 방식</p>
<ul>
<li>수신로와 송신로를 각각 둔 것이 아니고 한 경로를 기반으로 데이터를 보내기 때문에 보낼 때 충돌에 대해 대비하는 방식이다.</li>
</ul>
<h3 id="유선-lan을-이루는-케이블">유선 LAN을 이루는 케이블</h3>
<h4 id="트위스트-페어-케이블tp-케이블">트위스트 페어 케이블(TP 케이블)</h4>
<blockquote>
</blockquote>
<p>하나의 케이블처럼 보이지만 실제로는 여덟 개의 구리선을 두 개씩 꼬아서 묶은 케이블</p>
<p>** TP 케이블 종류**</p>
<ul>
<li>UTP 케이블(LAN 케이블) : 구리선을 실드 처리하지 않고 덮은 케이블</li>
<li>STP 케이블 : 구리선을 실드 처리하고 덮은 케이블</li>
</ul>
<p>📢 LAN 케이블을 꽂을 수 있는 커넥터 = <code>RJ-45 커넥터</code></p>
<h4 id="광섬유-케이블">광섬유 케이블</h4>
<blockquote>
</blockquote>
<p>광섬유로 만든 케이블</p>
<ul>
<li>레이저를 이용해서 통신하기 때문에 구리선과는 비교할 수 없을 만큼의 장거리 및 고속 통신이 가능하다.</li>
</ul>
<h3 id="무선lanieee80211">무선LAN(IEEE802.11)</h3>
<p>무선 LAN 장치는 수신과 송신에 같은 채널을 사용하기 때문에 <code>반이중화 통신</code>, <code>CSMA/CA</code>을 사용한다.</p>
<h4 id="반이중화-통신">반이중화 통신</h4>
<blockquote>
</blockquote>
<p><strong>반이중화 통신(Half duplex)</strong>는 양쪽 장치는 서로 통신할 수 있지만, 동시에는 통신할 수 없으며 한 번에 한 방향만 통신할 수 있는 방식</p>
<ul>
<li>장치가 신호를 수신하기 시작하면 응답하기 전에 전송이 완료될 때까지 기다려야 한다.</li>
<li>둘 이상의 장치가 동시에 전송하면 충돌이 발생하여 메시지가 손실되거나 왜곡될 수 있기 때문에 충돌 방지 시스템이 필요하다.</li>
</ul>
<h4 id="csmaca">CSMA/CA</h4>
<blockquote>
</blockquote>
<p><strong>CSMA/CA(Carrier Sense Multiple Access with Collision Avoidance)</strong>는 장치에서 데이터를 보내기 전에 일련의 과정을 기반으로 사전에 가능한 한 충돌을 방지하는 방식</p>
<h3 id="무선lan을-이루는-주파수">무선LAN을 이루는 주파수</h3>
<p>무선 LAN(WLAN, Wireless Local Area Network)은 <code>무선 신호 전달 방식을 이용하여 2대 이상의 장치를 연결하는 기술</code>을 의미한다.</p>
<p>공기 즉 비유도 매체로 주파수를 쏘아 무선 통신망을 구축하며 주파수 대역은 <code>2.4GHz</code>, <code>5GHz</code> 대역 중 하나를 사용해서 구축한다.</p>
<p><strong>2.4GHz</strong>는 장애물에 강한 특성을 가지고 있지만 전자레인지, 무선 등 전파 간섭이 일어나는 경우가 많다.
<strong>5GHz</strong>는 사용할 수 있는 채널 수도 많고 동시에 사용할 수 있으므로 상대적으로 깨끗한 전퐈 환경을 구축할 수 있다. 
➡️ 이러한 이유로 <code>5GHz</code> 대역을 사용하는 것이 좋다.</p>
<h3 id="와이파이wifi">와이파이(WIFI)</h3>
<blockquote>
</blockquote>
<p>전자기기들이 무선 LAN 신호에 연결할 수 있게 하는 기술</p>
<ul>
<li>WIFI를 사용하기 위해서는 무선 접속 장치(AP: Access Point) = 공유기가 있어야 한다. </li>
<li>공유기는 유선 LAN에 흐르는 신호를 무선 LAN 신호로 바꿔주어 신호가 닿는 범위 내에는 무선 인터넷을 사용할 수 있게 도와준다.</li>
</ul>
<h3 id="bssbasic-service-set">BSS(Basic Service Set)</h3>
<blockquote>
</blockquote>
<p>기본 서비스 집합을 의미하며 단순 공유기를 통해 네트워크에 접속하는 것이 아닌 동일 BSS 내에 있는 AP들과 징치들이 서로 통신이 가능한 구조</p>
<ul>
<li>하나의 AP만을 기반으로 구축이 되어 있어 사용자가 한 곳에서 다른 곳으로 자유롭게 이동하며 네트워크에 접속하는 것은 불가능하다.</li>
</ul>
<h3 id="essextended-service-set">ESS(Extended Service Set)</h3>
<blockquote>
</blockquote>
<p>하나 이상의 연결된 BSS 그룹</p>
<ul>
<li>장거리 무선 통신을 제공하며 BSS보다 더 많은 가용성과 이동성을 지원한다.</li>
<li>그러므로 사용자는 한 장소에서 다른 장소로 이동하며 중단 없이 네트워크에 계속 연결할 수 있다.</li>
</ul>
<h3 id="이더넷-프레임">이더넷 프레임</h3>
<blockquote>
</blockquote>
<p>이더넷(Ethernet) 네트워크에서 데이터를 전송할 때 사용하는 데이터 전송 단위</p>
<p>데이터 링크 계층에서 이더넷 프레임을 통해 전달받은 데이터의 에러를 검출하고 캡슐화한다.</p>
<hr>
<h2 id="1-5-계층-간-데이터-송수신-과정">1-5. 계층 간 데이터 송수신 과정</h2>
<p>하나의 컴퓨터에서 다른 컴퓨터로 데이터를 요청하게 된다면 애플리케이션 계층에서 전송 계층으로 보내는 요청(Request) 값들이 <code>캡슐화 과정</code>을 거쳐서 전달되고, 다시 링크 계층을 통해 해당 서버와 통신을 하고, 해당 서버의 링크 계층으로부터 애플리케이션까지 <code>비캡슐화 과정</code>을 거쳐 데이터가 전송된다.</p>
<h3 id="캡슐화-과정">캡슐화 과정</h3>
<blockquote>
</blockquote>
<p>상위 계층의 헤더와 데이터를 하위 계층의 데이터 부분에 포함시키고 해당 계층의 헤더를 삽입하는 과정</p>
<ul>
<li><p>애플리케이션 계층의 데이터가 전송 계층으로 전달되게 되면 <code>세그먼트</code> 또는 <code>데이터그램</code>화가 되어 TCP 헤더(L4)에 붙여지게 된다.</p>
</li>
<li><p>인터넷 계층으로 가면서 IP 헤더(L3)가 붙여지게 되고 <code>패킷</code>화가 된다.</p>
</li>
<li><p>링크 계층으로 전달되면서 프레임 헤더와 프레임 트레일러가 붙어 <code>프레임</code>화가 된다. </p>
<h3 id="비캡슐화-과정">비캡슐화 과정</h3>
<blockquote>
</blockquote>
<p>하위 계층에서 상위 계층으로 가며 각 계층의 헤더 부분을 제거하는 과정</p>
</li>
<li><p>캡술화된 데이터를 받게 되면 링크 계층에서부터 타고 올라가면서 <code>프레임화</code>된 데이터는 다시 <code>패킷화</code>를 거쳐 <code>세그먼트</code>, <code>데이터그램화</code>를 거쳐 <code>메시지화</code>가 되는 비캡술화 과정이 이루어지게 된다.</p>
</li>
<li><p>해당 과정을 전부 수행하고 나면 사용자에게 애플리케이션의 <code>PDU</code>인 메시지로 전달된다.</p>
</li>
</ul>
<hr>
<h1 id="2-pdu">2. PDU</h1>
<blockquote>
</blockquote>
<p><strong>PDU(Protocol Data Unit)</strong>란 네트워크의 어떠한 계층에서 계층으로 데이터가 전달될 때 한 덩어리의 단위</p>
<p>PDU는 제어 관련 정보들이 포함된 <code>헤더</code>, 데이터를 의미하는 <code>페이로드</code>로 구성되어 있으며 계층마다 부르는 명칭이 다르다.</p>
<p>** 계층별 부르는 PDU의 명칭**</p>
<ul>
<li>애플리케이션 계층 : 메시지</li>
<li>전송 계층 : 세그먼트(TCP), 데이터그램(UDP)</li>
<li>인터넷 계층 : 패킷</li>
<li>링크 계층 : 프레임(데이터 링크 계층), 비트(물리 계층)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 - 네트워크의 기초]]></title>
            <link>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@kkong_do/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Thu, 03 Oct 2024 14:27:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>** 네트워크(Network)**는 _<u>노드(Node)</u>_와 _<u>링크(Link)</u>_가 서로 연결되어 있으며 리소스를 공유하는 집합</p>
<p>네트워크란 여러 장치들이 서로 연결되어 데이터를 주고받으며 리소스를 공유하는 구조를 의미한다. 이러한 네트워크는 <strong>노드(Node)</strong>와 <strong>링크(Link)</strong>로 구성되어 있다.</p>
<ul>
<li><strong>노드(Node)</strong> : 서버, 라우터, 스위치 등의 네트워크 장치
각 노드는 데이터를 주고받는 역할을 하며 네트워크 안에서 자원을 공유하거나 정보를 교환한다.</li>
<li><strong>링크(Link)</strong> : 유선 또는 무선
유선 링크는 케이블, 광섬유 등을 통해 노드를 연결하며, 무선 링크는 Wi-Fi, 블루투스 등 전파를 이용해 노드를 연결한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/95bee1bd-83ed-4950-9e76-f5d8a5eb885d/image.jpg" alt=""></p>
<h1 id="1-처리량throughput과-지연-시간latency">1. 처리량(throughput)과 지연 시간(latency)</h1>
<p>네트워크를 구축할 때는 &#39;좋은&#39; 네트워크로 만드는 것이 중요하다. 여기서 말하는 <strong>&quot;좋은 네트워크&quot;</strong>란 많은 처리량(<strong>처리량⬆️</strong>)을 처리할 수 있으며 짧은 지연 시간(<strong>지연 시간⬇️</strong>), 적은 장애 빈도(<strong>장애 빈도⬇️</strong>), 좋은 보안(<strong>보안⬆️</strong>)을 갖춘 네트워크를 의미한다.</p>
<h2 id="1-1-처리량throughput">1-1. 처리량(throughput)</h2>
<blockquote>
</blockquote>
<p>** 처리량(Throughput) ** : 링크 내에서 성공적으로 전달된 데이터의 양, 처리한 트랙픽의 양</p>
<ul>
<li>&quot;많은 트랙픽을 처리한다. = 많은 처리량을 가진다.&quot;를 의미한다.</li>
<li>단위로는 <strong>bps(bits per second)</strong> : 초당 전송 또는 수신되는 비트 수</li>
</ul>
<p>처리량은 사용자들이 많이 접속할 때마다 커지는 트래픽, 네트워크 장치 간의 대역폭, 네트워크 중간에 발생하는 에러, 장치의 하드웨어 스펙에 영향을 받는다.</p>
<blockquote>
<p><strong>📢트래픽 :</strong> 특정 시점에 링크 내에 &#39;흐르는&#39; 데이터의 양
Ex) 서버에 저장된 파일(문서, 이미지, 동영상 등)을 클라이언트(사용자)가 다운로드할 때 발생되는 데이터의 누적량</p>
</blockquote>
<p>** 트래픽과 처리량의 차이점은?🤔 **</p>
<ul>
<li>트래픽이 많아졌다 : 흐르는 데이터가 많아졌다.</li>
<li>처리량이 많아졌다 : 처리되는 트래픽이 많아졌다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/a4f7884c-94de-4d56-99f4-fc5e1c16c259/image.jpg" alt=""></p>
<h2 id="1-2-지연-시간latency">1-2. 지연 시간(latency)</h2>
<blockquote>
</blockquote>
<p>** 지연시간(latency) **: 요청이 처리되는 시간, 어떤 메세지가 두 장치 사이를 왕복하는데 걸린 시간</p>
<p>지연 시간은 매체 타입(무선, 유선), 패킷 크기, 라우터의 패킷 처리 시간에 영향을 받는다.</p>
<p>** 패킷(Packet)과 라우터(Router)는?🤔**</p>
<ul>
<li>패킷은 네트워크를 통해 전송되는 데이터의 작은 조각</li>
<li>라우터는 여러 네트워크 간을 연결하고 패킷을 전달하는 장치</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/b84f17a4-c35f-4f20-8cb1-0aac269d115d/image.jpg" alt=""></p>
<hr>
<h1 id="2-네트워크-토폴로지network-topology와-병목-현상bottleneck">2. 네트워크 토폴로지(Network topology)와 병목 현상(BottleNeck)</h1>
<h2 id="2-1-네트워크-토폴로지network-topology의-종류">2-1. 네트워크 토폴로지(Network topology)의 종류</h2>
<h3 id="2-1-1-트리tree-토폴로지">2-1-1. 트리(Tree) 토폴로지</h3>
<blockquote>
</blockquote>
<p><strong>트리 토폴로지</strong>란 계층형 토폴로지, 트리 형태로 배치한 네트워크 구성</p>
<ul>
<li>노드의 추가, 삭제가 쉽지만 특정 노드에 트래픽이 집중될 때 하위 노드에 영향을 끼칠 수 있다.
<img src="https://velog.velcdn.com/images/kkong_do/post/4f2e9ec0-e1a5-43ab-b9ab-73722979a2bf/image.jpg" alt=""></li>
</ul>
<h3 id="2-1-2-버스bus-토폴로지">2-1-2. 버스(Bus) 토폴로지</h3>
<blockquote>
</blockquote>
<p><strong>버스 토폴로지</strong>란 중앙 통신 회선 하나에 여러 개의 노드가 연결되어 공유하는 네트워크 구성</p>
<ul>
<li>근거리 통신망(LAN)에서 사용된다.</li>
<li>설치 비용이 적고 신뢰성이 좋다. 또한, 중앙 통신 회선에 노드를 추가하거사 삭제하기가 쉽다.</li>
<li>하지만 ** 스푸핑**이 발생할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/4cb48bb6-0296-4594-a96a-2a1c248ccd0e/image.jpg" alt=""></p>
<p><strong>스푸핑(Spoofing)이란?</strong>🤔</p>
<blockquote>
<p><strong>스푸핑(Spoofing)</strong>은 LAN상에서 송신부의 패킷을 송신과 관련 없는 다른 호스트에 가지 않도록 하는 스위칭 기능을 마비시키거나 속여서 특정 노드에 해당 패킷이 오도록 처리하는 것</p>
</blockquote>
<ul>
<li>스푸핑을 적용하면 올바르게 수신부로 가야 할 패킷이 악의적인 노드에 전달되게 된다.</li>
</ul>
<h3 id="2-1-3-스타star-토폴로지">2-1-3. 스타(Star) 토폴로지</h3>
<blockquote>
</blockquote>
<p><strong>스타(Start) 토폴로지</strong>는 중앙에 있는 노드에 모두 연결된 네트워크 구성</p>
<ul>
<li>노드를 추가하거나 에러를 탐지하기가 쉽고 패킷의 충돌 발생 가능성이 적다.</li>
<li>어떤 노드에 장애가 발생해도 쉽게 에러를 발견할 수 있으며 장애 노드가 중앙 노드가 아닐 경우 다른 노드에 영향을 끼치는 것이 적다.</li>
<li>중앙 노드에 장애가 발생하게 될 경우 전체 네트워크를 사용할 수 없다는 점과 설치 비용이 고가라는 점이 존재한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/e68bcc48-610d-41fc-a3f0-a6da28adb2a9/image.jpg" alt=""></p>
<h3 id="2-1-4-링형ring-토폴로지">2-1-4. 링형(Ring) 토폴로지</h3>
<blockquote>
</blockquote>
<p><strong>링형 토폴로지</strong>는 각각의 노드가 양 옆의 두 노드와 연결하여 전체적으로 고리처럼 하나의 연속된 길을 통해 통신을 하는 네트워크 구성</p>
<ul>
<li>데이터는 노드에서 노드로 이동을 하게 된다. 또한, 각각의 노드는 고리 모양의 길을 통해 패킷을 처리한다.</li>
<li>노드 수가 증가되어도 네트워크 상의 손실이 거의 없고 충돌이 발생되는 가능성이 적다.</li>
<li>노드의 고장 발견을 쉽게 찾을 수 있지만 네트워크 구성 변경이 어렵다는 점과 회선에 장애가 발생하면 전체 네트워크에 영향을 끼친다는 점이 존재한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/98f57c6a-5428-421b-aaa9-63ed8d8446ec/image.jpg" alt=""></p>
<h3 id="2-1-5-메시mesh-토폴로지">2-1-5. 메시(Mesh) 토폴로지</h3>
<blockquote>
</blockquote>
<p><strong>메시 토폴로지</strong>는 망형 토폴로지라고도 하며 그물망처럼 연결되어있는 네트워크 구성</p>
<ul>
<li>한 단말 장치에 장애가 발생해도 여러 개의 경로가 존재하므로 네트워크를 계속 사용할 수 있으며 트래픽도 분산 처리가 가능하다.</li>
<li>노드의 추가가 어렵고 구축 비용과 운용 비용이 고가인 단점이 존재한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/16fa9eed-11e7-4492-bdfc-507f840ee7b6/image.jpg" alt=""></p>
<h2 id="2-2-병목-현상bottleneck">2-2. 병목 현상(BottleNeck)</h2>
<blockquote>
</blockquote>
<p><strong>병목 현상(BottleNeck)</strong>은 전체 시스템의 성능이나 용량이 하나의 구성 요소로 인해 제한을 받는 현상</p>
<ul>
<li>병목 현상을 해결하기 위해서는 네트워크가 어떤 토폴로지를 갖는지, 어떠한 경로로 이루어져 있는지 알아야 한다.</li>
</ul>
<hr>
<h1 id="3-네트워크-분류">3. 네트워크 분류</h1>
<h2 id="3-1-lanlocal-area-network">3-1. LAN(Local Area Network)</h2>
<blockquote>
</blockquote>
<p><strong>LAN</strong>이란 근거리 통신망</p>
<ul>
<li>건물이나 캠퍼스 같은 좁은 공간에서 운영된다.</li>
<li>전송 속도가 빠르고 혼잡하지 않는다.</li>
</ul>
<h2 id="3-2-manmetropolitan-area-network">3-2. MAN(Metropolitan Area Network)</h2>
<blockquote>
</blockquote>
<p><strong>MAN</strong>이란 대도시 지역 네트워크</p>
<ul>
<li>도시 같은 넓은 지역에서 운영된다.</li>
<li>전송 속도는 평균이며 LAN보다는 더 많이 혼잡하다.</li>
</ul>
<h2 id="3-3-wanwide-area-network">3-3. WAN(Wide Area Network)</h2>
<blockquote>
</blockquote>
<p><strong>WAN</strong>이란 광역 네트워크</p>
<ul>
<li>국가 또는 대륙 같은 더 넓은 지역에서 운영된다.</li>
<li>전송 속도는 낮으며 MAN보다 더 혼잡하다.</li>
</ul>
<hr>
<h1 id="4-네트워크-성능-분석-명령어">4. 네트워크 성능 분석 명령어</h1>
<p>애플리케이션 코드상에서 문제가 없는데 사용자가 서비스로부터 데이터를 가져오지 못하는 상황이 발생되기도 한다. 이러한 경우는 네트워크 병목 현상이 발생한 경우일 수도 있는데 네트워크 병목 현상의 주된 원인에 대해 먼저 알아보자!</p>
<h3 id="네트워크-병목-현상의-원인">네트워크 병목 현상의 원인</h3>
<ul>
<li>네트워크 대역폭</li>
<li>네트워크 토폴로지</li>
<li>서버 CPU, 메모리 사용량</li>
<li>비효율적인 네트워크 구성</li>
</ul>
<p>네트워크 관련된 테스트와 네트워크와 무관한 테스트를 통해서 &#39;네트워크로부터 발생한 문제점&#39;인 것을 확인한 후 네트워크 성능 분석을 진행하여야 한다. 네트워크 성능 분석에 사용되는 명령어들에 대하여 알아보도록 하자!</p>
<h2 id="4-1-pingpacket-inernet-groper">4-1. Ping(Packet INernet Groper)</h2>
<blockquote>
</blockquote>
<p><strong>Ping</strong>은 네트워크 상태를 확인하려는 대상 노드를 향해 일정 크키의 패킷을 전송하는 명령어</p>
<ul>
<li>Ping 명령어를 통해 해당 노드의 패킷 수신 상태와 도달하기까지의 시간 등을 알 수 있다.</li>
<li>해당 노드까지 네트워크가 잘 연결되어 있는지 확인이 가능하다.</li>
</ul>
<p>Ping은 TCP/IP 프로토콜 중에 ICMP 프로토콜을 통해 동작하며, ICMP를 지원하지 않는 기기를 대상으로는 실행할 수 없고 네트워크 정책 상 ICMP나 traceroute를 차단하는 대상일 경우 ping 테스트가 불가능하다.</p>
<p>ping [IP주소 또는 도메인 주소]로 실행한다.</p>
<h2 id="4-2-netstat">4-2. netstat</h2>
<blockquote>
</blockquote>
<p><strong>netstat</strong>는 접속되어 있는 서비스들의 네트워크 상태를 표시하는데 사용되는 명령어</p>
<ul>
<li>네트워크 접속, 라우팅 테이블, 네트워크 프로토콜 등 리스트를 보여준다.</li>
<li>서비스의 포트가 열려있는지 확인하는 용도로 많이 쓰인다.</li>
</ul>
<h2 id="4-3-nslookup">4-3. nslookup</h2>
<blockquote>
</blockquote>
<p><strong>nslookup</strong>은 DNS에 관련된 내용을 확인하기 위해 사용되는 명령어</p>
<ul>
<li>특정 도메인에 매핑된 IP를 확인하기 위해서 사용한다.</li>
</ul>
<h2 id="4-4-tracerttraceroute">4-4. tracert(traceroute)</h2>
<blockquote>
</blockquote>
<p><strong>tracert(traceroute)</strong>는 목적지 노드까지 네트워크 경로를 확인할 때 사용하는 명령어</p>
<ul>
<li>목적지 노드까지 구간들 중 어느 구간에서 응답 시간이 느려지는 등을 확인하기 위해서 사용한다.</li>
</ul>
<h1 id="1-5-네트워크-프로토콜-표준화">1-5. 네트워크 프로토콜 표준화</h1>
<blockquote>
</blockquote>
<p><strong>네트워크 프로토콜</strong>은 다른 장치들끼리 데이터를 주고받기 위해 설정된 공통된 인터페이스</p>
<ul>
<li>IEEE 또는 IETF라는 표준화 단체가 네트워크 프로토콜을 정한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제 - CPU 스케줄링 알고리즘]]></title>
            <link>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-s59dpe2g</link>
            <guid>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-s59dpe2g</guid>
            <pubDate>Sun, 29 Sep 2024 16:41:42 GMT</pubDate>
            <description><![CDATA[<p>CPU 스케줄러는 CPU 스케줄링 알고리즘에 따라 프로세스에 해야 하는 일을 스레드 단위로 CPU에 할당한다.</p>
<p>프로그램이 실행될 때는 CPU 스케줄링 알고리즘이 어떤 프로그램에 CPU 소유권을 줄 것인지 결정하며 <strong>CPU 스케줄링 알고리즘</strong>은 <strong><em>CPU 이용률은 높게</em></strong>, <strong><em>주어진 시간에 많은 일을 하게</em></strong>, <strong><em>준비 큐(ready queue)에 있는 프로세스는 적게</em></strong>, <strong><em>응답 시간은 짧게</em></strong> 설정하는 것을 목표로 한다.  </p>
<p>CPU 스케줄링 알고리즘은 크게 <strong><em>비선점형(non-preemptive) 방식</em></strong>와 <strong><em>선점형(preemptive) 방식</em></strong>으로 이루어져 있다.</p>
<hr>
<h2 id="1-비선점형-방식non-preemptive">1. 비선점형 방식(non-preemptive)</h2>
<p><strong>비선점형 방식(non-preemptive)</strong>는 프로세스가 스스로 CPU 소유권을 포기하는 방식이며 강제로 프로세스를 중지하지 않기 떄문에 컨텍스트 스위칭(Context Switching)으로 인한 부하가 적다.</p>
<h3 id="1-1-비선점형-방식의-종류">1-1. 비선점형 방식의 종류</h3>
<h4 id="fcfsfirst-come-first-served">FCFS(First Come, First Served)</h4>
<blockquote>
</blockquote>
<p>가장 먼저 온 것을 가장 먼저 처리하는 알고리즘</p>
<p>길게 수행되는 프로세스 때문에 &#39;준비 큐에서 오래 기다리는 현상(convoy effect)가 발생하는 단점이 있다.</p>
<h4 id="sjfshortest-job-first">SJF(Shortest Job First)</h4>
<blockquote>
</blockquote>
<p>실행 시간이 가장 짧은 프로세스를 가장 먼저 실행하는 알고리즘</p>
<p>긴 시간을 가진 프로세스가 실행되지 않는 현상(starvation)이 일어나며 평균 대기 시간이 가장 짧다. 하지만 실제로는 실해 시간을 알 수 없기 때문에 과거의실행했던 시간을 토대로 추측해서 사용한다.</p>
<h4 id="우선순위">우선순위</h4>
<blockquote>
</blockquote>
<p>기존 SJF를 보완한 알고리즘으로써 오래된 작업일수록 우선순위를 높이는 방법을 사용해 보완한 알고리즘</p>
<hr>
<h2 id="2-선점형-방식preemptive">2. 선점형 방식(preemptive)</h2>
<p><strong>선점형 방식(preemptive)</strong>는 현대 운영체제가 쓰는 방식으로서 지금 사용하고 있는 프로세스를 알고리즘에 의해 중단시켜 버리고 강제로 다른 프로세스에 CPU 소유권을 할당하는 방식을 말한다.</p>
<h3 id="2-1-선점형-방식의-종류">2-1. 선점형 방식의 종류</h3>
<h4 id="라운드-로빈rr-round-robin">라운드 로빈(RR, Round Robin)</h4>
<p>현대 컴퓨터가 쓰는 선점형 알고리즘 스케줄링 방법으로서 각 프로세스는 동일한 할당 시간을 주고 그 시간 안에 끝나지 않으면 다시 준비 큐(Ready Queue)의 뒤로 보내는 알고리즘을 의미한다.</p>
<p>주의해야할 점은 할당 시칸이 너무 크게 하면 FCFS가 되고 짧으면 컨텍스트 스위칭이 잦아져서 오버헤드(=비용)이 커진다.
일반적으로는 전체 작업 시간은 길어지지만 평균 응답 시간은 짧아진다는 특징이 있다.</p>
<p>라운드 로빈 알고리즘은 로드밸런서에서 트래픽 분산 알고리즘으로 사용된다.</p>
<h4 id="srfshortest-remaining-time-first">SRF(Shortest Remaining Time First)</h4>
<p>프로세스 중에서 남은 실행 시간이 가장 짧은 프로세스를 우선적으로 실행하는 방식으로서, 작업을 진행하는 중간에 더 짧은 작업이 들어오면 수행하던 프로세스를 중지하고 해당 프로세스를 수행하는 알고리즘을 의미한다.</p>
<h4 id="다단계-큐multilevel-queue">다단계 큐(Multilevel Queue)</h4>
<p>CPU 스케줄링 알고리즘 중 하나로 프로세스를 우선순위에 따라 여러 개의 큐로 분리하여 각각의 큐에 다른 스케줄링 방식을 적용하는 방식이다.
각 큐는 서로 다른 프로세스 그룹을 처리하며, 각 큐의 프로세스는 독립적으로 스케줄링되고 프로세스 그룹에 따라 우선순위와 처리 방법을 다르게 적용하기 때문에, 우선순위가 높은 작업을 먼저 처리할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제 - 프로세스와 스레드]]></title>
            <link>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</guid>
            <pubDate>Sun, 29 Sep 2024 14:05:01 GMT</pubDate>
            <description><![CDATA[<p><strong>프로세스(Process)</strong>는 컴퓨터에서 실행되고 있는 프로그램을 의미하며, CPU 스케줄링의 대상이 되는 작업(Task)라는 용어와 거의 같은 의미로 사용되고 있다. <strong>스레드(Thread)</strong>는 프로세스 내 작업의 흐름을 의미한다.  </p>
<p>프로그램이 메모리(RAM)로 올라가면 프로세스가 되는 인스턴스화가 일어나고, 이후 운영체제의 CPU 스케줄러에 따라 CPU가 프로세스를 실행한다.</p>
<hr>
<h1 id="1-프로세스의-상태">1. 프로세스의 상태</h1>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/63000b8a-6cb1-46f3-a45b-3a01ec87b3eb/image.jpg" alt=""></p>
<h2 id="생성create-상태">생성(Create) 상태</h2>
<p><strong><em>프로세스가 생성된 상태</em></strong>를 의미하며 fork() 또는 exec() 함수를 통해서 생성된다. 이때 PCB가 할당된다.</p>
<blockquote>
</blockquote>
<p>📢 <strong>PCB(프로세스 제어 블록, Process Control Block)</strong>는 운영체제가 프로세스에 대한 중요한 정보를 저장하는 데이터 구조</p>
<p><strong>fork()</strong>는 부모 프로세스의 주소 공간을 그대로 복사하여 새로운 자식 프로세스를 생성하는 함수를 의미하며 주소 공간만 복사할 분이지 부모 프로세스의 비동기 작업 등을 상속하지 않는다. <strong>exec()</strong>는 새롭게 프로세스를 생성하는 함수를 의미한다.</p>
<h2 id="대기ready-상태-대기-중단ready-suspended-상태">대기(Ready) 상태, 대기 중단(Ready Suspended) 상태</h2>
<p><strong>대기(Ready) 상태</strong>는 <strong><em>메모리 공간이 충분하면 메모리를 할당받고 아니면 아닌 상태로 대기하고 있는 상태</em></strong>이며 <strong><em>CPU 스케줄러로부터 CPU 소유권이 넘어오기를 기다리는 상태</em></strong>를 의미한다.</p>
<p><strong>대기 중단(Ready Suspended)</strong> <strong><em>메모리 부족으로 일시 중단된 상태</em></strong>를 의미한다.</p>
<h2 id="실행run-상태">실행(Run) 상태</h2>
<p><strong>실행(Run) 상태</strong>는 <strong><em>CPU 소유권과 메모리를 할당받고 인스트럭션(명령)을 수행 중인 상태</em></strong>를 의미한다. ➡️ <strong>&#39;CPU burst&#39;</strong>가 일어났다고 표현한다.</p>
<h2 id="중단blocked-상태-일시-중단-blocked-suspendes-상태">중단(Blocked) 상태, 일시 중단 (Blocked Suspendes) 상태</h2>
<p><strong>중단(Blocked) 상태</strong>는 <strong><em>어떤 이벤트가 발생한 이후 기다리며 프로세스가 차단되 상태</em></strong>를 의미한다. I/O 디바이스에 의한 인터럽트로 많이 발생하는 상태이기도 하다.</p>
<p><strong>일시 중단(Blocked Suspendes) 상태</strong>는 <strong><em>중단된 상태에서 프로세스가 실행되려고 했지만 메모리 부족으로 일시 중단된 상태</em></strong>를 의미한다. </p>
<h2 id="종료terminated-상태">종료(Terminated) 상태</h2>
<p><strong>종료(Terminated) 상태</strong>는 <strong><em>메모리와 CPU 소유권을 모두 놓고 가는 상태</em></strong>를 의미한다. 자연스럽게 종료되는 경우도 있지만 부모 프로세스가 자식 프로세스를 강제시키는 비자발적 종료(abort)로 종료되는 경우도 있다. 이러한 경우는 자식 프로세스에 할당된 자원의 한계치를 넘어섰거나 부모 프로세스가 종료되거나 사용자가 <code>process.kill</code>등 여러 명령어로 프로세스를 종료할 때 발생한다.</p>
<hr>
<h1 id="2-프로세스의-메모리-구조">2. 프로세스의 메모리 구조</h1>
<p>운영체제는 프로세스에 적절한 메모리를 할당하며 다음 구조를 기반으로 할당하게 된다.
<img src="https://velog.velcdn.com/images/kkong_do/post/6eb7f4be-4321-4a5d-bf3e-c20516c5d794/image.jpg" alt=""></p>
<h2 id="2-1-스택과-힙stack-and-heap">2-1. 스택과 힙(Stack And Heap)</h2>
<p><strong>스택(Stack)</strong>과 <strong>힙(Heap)</strong>은 동적 할당이 되고 동적 할당은 런타입 단계에서 메모리를 할당받는 것을 의미한다.</p>
<p><strong>스택(Stack)</strong>은 <strong><em>지역 변수, 매개 변수, 실행되는 함수에 의해 늘어들거나 줄어드는 메모리 영역</em></strong>을 의미한다. 함수가 호출될 때마다 호출될 때의 환경 등 특정 정보가 스택에 계속해서 저장된다. 
또한, 재귀 함수는 호출될 때마다 새로운 스택 프레임(함수가 호출될 때 함수의 실행 상태를 저장하는 메모리 영역)을 생성하여 함수 내에서 사용하는 매개변수나 지역 변수들이 독립적으로 관리된다. 이로 인해 각 재귀 호출에서 사용하는 변수들이 다른 호출의 변수와 간섭하지 않으며 서로 다른 변수처럼 동작한다</p>
<p><strong>힙(Heap)</strong>은 동적으로 할당되는 변수들을 담으며, <code>malloc()</code>와 <code>free()</code> 함수를 통해 관리할 수 있고 <strong><em>동적으로 관리되는 자료 구조(Ex. 연결 리스트, ArrayList, 그래프 등)의 경우 힙 영역을 사용</em></strong>한다.</p>
<h2 id="2-2-데이터-영역과-코드-영역">2-2. 데이터 영역과 코드 영역</h2>
<p>데이터 영역과 코드영역은 정적으로 할당되는 영역으로, 정적 할당은 컴파일 단계에서 메모리를 할당하는 것을 의미한다.
데이터 영역은 BSS Segment, Data Segment, Code/Test Segment로 나누어서 저장이 된다.</p>
<h3 id="데이터-영역">데이터 영역</h3>
<ul>
<li><strong>BSS Segment</strong>는 <strong>전역 변수 혹은 static, const로 선언</strong>되어 있고 <strong>0으로 초기화 또는 초기화가 어떠한 값으로도 되어 있지 않은 변수</strong>들이 할당되는 영역이다.</li>
<li><strong>Data Segment</strong>는 <strong>전역 변수 혹은 static, const로 선언</strong>되어 있고 <strong>0이 아닌 값으로 초기화된 변수</strong>가 할당되는 영역이다.</li>
<li><strong>Code Segment</strong>는 프로그램의 코드가 들어가는 영역이다.</li>
</ul>
<hr>
<h1 id="3-pcbprocess-control-block">3. PCB(Process Control Block)</h1>
<p>앞서 PCB에 대하여 짧게 정의 정도만 말하고 넘어갔는데 PCB에 대하여 조금 더 자세히 알아보자!!</p>
<blockquote>
</blockquote>
<p><strong>PCB(프로세스 제어 블록, Process Control Block)</strong>는 운영체제가 프로세스에 대한 중요한 정보를 저장하는 데이터 구조</p>
<blockquote>
</blockquote>
<p><strong>PCB(프로세스 제어 블록, Process Control Block)</strong> 운영체제에서 프로세스에 대한 메타 데이터를 저장하는 <strong><em>&#39;데이터&#39;</em></strong></p>
<p>프로세스 제어 블록이라고도 하며 프로세스가 생성되면 운영체제는 해당 PCB를 생성한다.</p>
<p>프로그램이 실행되면 프로세스가 생성되고 프로세스 주소 값들에 앞서 설명한 스택, 힙 등의 구조를 기반으로 메모리에 할당이 된다. 그러고 나서 이 프로세스의 메타데이터들이 PCB에 저장되어 관리된다. 이는 프로세스의 중요한 정보를 포함하고 있기 때문에 일반 사용자가 접근하지 못하도록 커널 스택의 가장 앞부분에서 관리된다.</p>
<blockquote>
<p>📢 <strong>메타데이터</strong>는 <strong><em>데이터에 관한 구조화된 데이터이자 데이터를 설명하는 작은 데이터</em></strong></p>
</blockquote>
<ul>
<li>대량의 정보 가운데에서 찾고 있는 정보를 효율적으로 찾아내서 이용하기 위해 일정한 규칙에 따라 콘텐츠에 대해 부여되는 데이터가 바로 <strong>메타 데이터</strong>이다.</li>
</ul>
<h2 id="3-1-pcb의-구조">3-1. PCB의 구조</h2>
<p>PCB는 프로세스 스케줄링 상태, 프로세스 ID 등의 다음과 같은 정보로 이루어져 있다.</p>
<h3 id="pcb의-구조-종류-중-몇가지">PCB의 구조 종류 중 몇가지</h3>
<table>
<thead>
<tr>
<th>종류</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>프로세스 스케줄링 상태</strong></td>
<td>&#39;준비&#39;, &#39;일시중단&#39; 등의 프로세스가 CPU에 대한 소유권을 얻은 이후의 상태</td>
</tr>
<tr>
<td><strong>프로세스 ID</strong></td>
<td>프로세스 ID, 해당 프로세스의 자식 프로세스 ID</td>
</tr>
<tr>
<td><strong>프로세스 권한</strong></td>
<td>컴퓨터 자원 또는 I/O 디바이스에 대한 권한 정보</td>
</tr>
<tr>
<td>---</td>
<td></td>
</tr>
</tbody></table>
<h2 id="3-2-컨텍스트-스위칭context-switching">3-2. 컨텍스트 스위칭(Context Switching)</h2>
<blockquote>
</blockquote>
<p><strong>컨텍스트 스위칭(Context Switching)</strong>는 PCB를 기반으로 프로세스의 상태를 저장하고 로드시키는 과정</p>
<p>컨텍스트 스위칭은 한 프로세스에 할당된 시간이 끝나거나 인터럽트에 의해 발생한다. 현대 컴퓨터는 멀티코어의 CPU를 가지고 있기 때문에 한 시점에 한 개의 프로그램이라는 설명은 틀린 설명이지만 컨텍스트 스위칭을 설명할 때는 싱글코어(Single Core)를 기준으로 설명한다.</p>
<p>컴퓨터는 많은 프로그램을 동시에 실행하는 것처럼 보이지만 어떠한 시점에서 실행되고 있는 프로세스는 단 한가지이며, 많은 프로세스가 동시에 구동되는 것처럼 보이는 것은 다른 프로세스와의 컨텍스트 스위칭이 아주 빠른 속도로 실행되고 있기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/30447140-66cd-4589-85ea-f38e9f96c50f/image.jpg" alt=""></p>
<p>한 개의 프로세스 A가 실행하다가 멈추고 프로세스 A의 PCB를 저장하고 다시 프로세스 B를 로드하여 실행한다. 그리고 다시 프로세스 B의 PCB를 저장하고 프로세스 A의 PCB를 로드한다.
컨텍스트 스위칭이 일어날 때 그림처럼 유휴 시간 (idle time)이 발생하는 것을 볼 수 있는데 유휴 시간외에 캐시 미스(Cache Miss)라고 하는 비용이 든다.</p>
<h3 id="캐시미스cache-miss">캐시미스(Cache Miss)</h3>
<blockquote>
<p><strong>캐시미스</strong> CPU가 필요한 데이터를 캐시에서 찾지 못하고 더 느린 메모리 계층(예: 주기억장치 RAM)에서 데이터를 가져와야 하는 상황</p>
</blockquote>
<p>컨텍스트 스위칭이 일어날 때 프로세스가 가지고 있는 메모리 주소가 그대로 있으면 잘못된 주소 변환이생기므로 캐시클리어(캐시를 비우는 작업) 과정을 겪게 되고 이 때문에 캐시미스가 발생한다. </p>
<h3 id="스레드thread에서의-컨텍스트-스위칭">스레드(Thread)에서의 컨텍스트 스위칭</h3>
<p>컨텍스트 스위칭은 스레드에서도 일어나게 되는데 스레드는 스택 영역을 제외한 모든 메모리를 공유하기 때문에 스레드 컨텍스트 스위칭의 경우 비용이 더 적고 시간도 적게 걸린다는 점이 있다.</p>
<hr>
<h1 id="4-멀티프로세싱">4. 멀티프로세싱</h1>
<blockquote>
</blockquote>
<p><strong>멀티프로세싱</strong>은 여러 개의 &#39;프로세스&#39; = 멀티프로세스를 통해 동시에 두 가지 이상의 일을 수행할 수 있는 것</p>
<p>하나 이상의 일을 병렬로 처리할 수 있으며, 특정 프로세스의 메모리, 프로세스 중 일부에 문제가 발생되더라도 다른 프로세스를 이용해서 처리할 수 있으므로 <strong>신뢰성이 높은 강점</strong>이 있다.</p>
<p>하드웨어적 관점🔭으로 봤을 때 여러 개의 프로세서로 작업을 처리하는 것을 의마하기도 한다.</p>
<h2 id="4-1-웹-브라우저">4-1. 웹 브라우저</h2>
<p>웹브라우저는 멀티프로세스 구조를 가지고 있으며 다음과 같은 종류를 가지고 있다.</p>
<table>
<thead>
<tr>
<th>종류</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>브라우저 프로세스</strong></td>
<td>주소 표시줄, 북마크 막대, 뒤로 가기 버튼, 앞으로 가기 버튼 등을 담당하며 네트워크 요청이나 파일 접근 같은 권한 담당</td>
</tr>
<tr>
<td><strong>랜더러 프로세스</strong></td>
<td>웹 사이트가 보이는 부분의 모든 것을 제어하는 것을 담당</td>
</tr>
<tr>
<td><strong>플로그인 프로세스</strong></td>
<td>웹 사이트에서 사용하는 플로그인을 제어하는 것을 담당</td>
</tr>
<tr>
<td><strong>GPU 프로세스</strong></td>
<td>GPU를 이용해서 화면을그리는 부분을 제어하는 것을 담당</td>
</tr>
</tbody></table>
<h2 id="4-2-ipcinter-process-communication">4-2. IPC(Inter Process Communication)</h2>
<p>멀티프로세스는 IPC가 가능하며, IPC는 프로세스끼리 데이터를 주고 받고 공유 데이터를 관리하는 메커니즘을 의미한다.</p>
<blockquote>
</blockquote>
<p><strong>IPC(Inter-Process Communication)</strong>는 프로세스 간 통신을 의미하며, 여러 프로세스가 데이터를 주고받거나 공유 데이터를 관리할 수 있게 해주는 메커니즘</p>
<p>예를 들어 클라이언트는 데이터를 요청하고 서버는 클라이언트 요청에 응답하는 것 또한 IPC의 한 예로 들 수 있다.</p>
<p>IPC의 종류로는 공유 메모리, 파일, 소켓, 익명 파이프, 명명된 파이프, 메시지 큐가 있으며 이들은 모두 메모리가 완전히 공유되는 스레드보다는 속도가 떨어진다. 이제 IPC의 종류를 알아보도록 하자!!</p>
<h3 id="4-2-1-ipc의-종류">4-2-1. IPC의 종류</h3>
<h4 id="1-공유-메모리shared-memeory">1. 공유 메모리(Shared Memeory)</h4>
<blockquote>
<p>여러 프로세스에 동일한 메모리 블록에 대한 접근 권한이 부여되어 프로세스가 서로 통신할 수 있도록 공유 메모리를 생성해서 통신하는 것</p>
</blockquote>
<p>기본적으로 각 프로세스의 메모리를 다른 프로세스가 접근할 수 없지만 공유 메모리를 통해 여러 프로세스가 하나의 메모리를 공유할 수 있다.</p>
<p>메모리 자체를 공유하기 때문에 불필요한 데이터 복사의 오버헤드가 발생해자 않아 가장 빠르고 같은 메모리 영역을 여러 프로세스가 공유하기 때문에 동기화가 필요하다.</p>
<p>하드웨어적인 관점으로 공유 메모리는 CPU가 접근할 수 있는 RAM을 의미한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/d2710bc7-83a9-4057-bfe0-12ee85f88318/image.jpg" alt=""></p>
<h4 id="2-파일">2. 파일</h4>
<blockquote>
</blockquote>
<p>디스크에 저장된 데이터 또는 파일 서버에서 제공하는 데이터이며 이를 기반으로 프로세스 간 통신을 진행</p>
<h4 id="3-소켓">3. 소켓</h4>
<blockquote>
</blockquote>
<p>동일한 컴퓨터의 다른 프로세스나 네트워크의 다른 컴퓨터로 네트워크 인터페이스를 통해 전송하는 데이터</p>
<ul>
<li>소켓의 종류로는 TCP와 UDP가 있다.</li>
</ul>
<h4 id="4-익명-파이프unnamed-pipe">4. 익명 파이프(Unnamed Pipe)</h4>
<blockquote>
</blockquote>
<p>프로세스 간에 FIFO 방식으로 읽히는 임시 공간인 파이프를 기반으로 데이터를 주고받으며, 단방향 방식의 읽기 전용, 쓰기 전용 파이프를 만들어 작동하는 방식</p>
<ul>
<li>부모, 자식 프로세스 간에만 사용할 수 있으며 다른 네트워크상에서는 사용이 불가능</li>
</ul>
<h4 id="5-명명된-파이프named-pipe">5. 명명된 파이프(Named Pipe)</h4>
<blockquote>
</blockquote>
<p>파이프 서버와 하나 이상의 파이프 클라이언트 간의 통신을 위한 명명된 단방향 또는 양방향 파이프</p>
<ul>
<li>클라리언트/서버 통신을 위한 별도의 파이프를 제공하며 여러 파이프를 동시에 사용할 수 있다.</li>
<li>컴퓨터의 프로세스끼리 혹은 다른 네트워크 상의 컴퓨터와도 통신을 할 수 있다.
<img src="https://velog.velcdn.com/images/kkong_do/post/bc922300-f96f-4d6f-bfcd-e27697e37b93/image.jpg" alt=""></li>
</ul>
<h4 id="6-메시지-큐message-queue">6. 메시지 큐(Message Queue)</h4>
<blockquote>
</blockquote>
<p>메시지를 큐(queue) 데이터 구조 형태로 관리하는 방식</p>
<ul>
<li>커널의 전역 변수 형태 등 커널에서 전역적으로 관리되며 다른 IPC 방식에 비해서 사용 방법이 매우 직관적이고 간단하며 다른 코드의 수정 없이 단지 몇 줄의 코드를 추가시켜 간단하게 메시지 큐에 접근할 수 있는 장점이 있다.</li>
</ul>
<hr>
<h1 id="5-스레드와-멀티스레팅">5. 스레드와 멀티스레팅</h1>
<h2 id="스레드">스레드</h2>
<blockquote>
</blockquote>
<p><strong><em>프로세스의 실행 가능한 가장 작은 단위</em></strong></p>
<ul>
<li>프로세스는 여러 스레드를 가지고 있을 수 있다.
<img src="https://velog.velcdn.com/images/kkong_do/post/a3b2cfe5-192c-4adb-b777-03f2896b3505/image.jpg" alt=""></li>
</ul>
<h2 id="멀티스레딩">멀티스레딩</h2>
<blockquote>
</blockquote>
<p>프로세스 내 작업을 여러 개의 스레드, 멀티스레드로 처리하는 기법</p>
<ul>
<li>스레드끼리 서로 자원을 공유하기 때문에 효율성이 높다.</li>
<li>중단되지 않은 빠른 처리가 가능하며, 동시성에도 큰 장점이 있다.</li>
<li>하지만 한 스레드에 문제가 생기면 다른 스레드에서도 영향을 끼쳐 스레드로 이루어져 있는 프로세스에 영향을 줄 수 있다는 단점이 있다.</li>
</ul>
<blockquote>
</blockquote>
<p>📢 <strong>동시성</strong> : 서로 독립적인 작업들을 작은 단위로 나누고 동시에 실행되는 것처럼 보여주는 것</p>
<hr>
<h1 id="6-공유-자원과-임계-영역">6. 공유 자원과 임계 영역</h1>
<h2 id="6-1-공유-자원shared-resource">6-1. 공유 자원(Shared Resource)</h2>
<blockquote>
</blockquote>
<p>시스템 안에서 각 프로세스, 스레드가 함께 접근할 수 있는 모니터, 프린터, 메모리, 파일, 데이터 등의 자원이나 변수 등을 의미한다.</p>
<p>공유 자원을 두 개 이상의 프로세스가 동시에 읽거나 쓰는 상황을 <strong><em>경쟁 상태(race condition)</em></strong>라고 하며, 동시에 접근을 시도할 때 접근의 타이밍이나 순서 등이 결괏값에 영향을 줄 수 있는 상태를 의미한다.</p>
<h2 id="6-2-임계-영역critical-section">6-2. 임계 영역(Critical Section)</h2>
<blockquote>
</blockquote>
<p>둘 이상의 프로세스, 스레드가 공유 자원에 접근할 때 순서 등의 이유로 결과가 달라지는 코드 영역</p>
<h3 id="임계영역을-해결하기-위한-방법">임계영역을 해결하기 위한 방법</h3>
<p>임계 영역(Critical Section) 문제를 해결하기 위해 사용하는 대표적인 방법으로 뮤텍스(Mutex), 세마포어(Semaphore), 그리고 <strong>모니터(Monitor)</strong>가 있으며, 이들 모두는 상호 배제(Mutual Exclusion), 한정 대기(Bounded Waiting), 융통성(Flexibility) 조건을 만족한다. 
이 세 가지 방법의 핵심 메커니즘은 <strong>잠금(Lock)</strong>, 즉, 임계 영역에 하나의 프로세스가 들어가면, 다른 프로세스는 해당 프로세스가 임계 영역에서 나올 때까지 기다렸다가 임계 영역에 들어가게 되는 방법이다.</p>
<blockquote>
</blockquote>
<p>📢 <strong>상호배제</strong> : 한 프로세스가 임계 영역에 들어갔을 때 다른 프로세스는 들어갈 수 없다.</p>
<blockquote>
</blockquote>
<p>📢 <strong>한정 대기</strong> : 특정 프로세스가 영원히 임계 영역에 들어가지 못하면 안된다.</p>
<blockquote>
</blockquote>
<p>📢 <strong>융통성</strong> : 만약 어떠한 프로세스도 임계 영역을 사용하지 않는다면 임계 영역 외부의 어떠한 프로세스도 들어갈 수 있으며 이 때 프로세스끼리 서로 방해하지 않는다.</p>
<h3 id="뮤텍스">뮤텍스</h3>
<blockquote>
</blockquote>
<p>프로세스나 스레드가 공유 자원을 <code>lock()</code>을 통해 잠금을 설정하고 사용한 후에는 <code>unlock()</code>을 통해 잠금을 해제하는 객체</p>
<p>잠금이 설정되면 다른 프로세스나 스레드는 잠긴 코드 영역에 접근할 수 없고 해제는 다른 프로세스나 스레드가 코드 영역에 접근할 수 있게 된다. 뮤텍스는 잠금 또는 잠금 해제라는 상태만을 가진다.</p>
<h3 id="세마포어">세마포어</h3>
<blockquote>
</blockquote>
<p>일반화된 뮤텍스이며 간단한 정수 값과 두 가지 함수 <code>wait()</code>및 <code>signal()</code>로 공유 자원에 대한 접근을 처리한다.</p>
<p><code>wait()</code>는 P함수라고도 하며, 자신의 차례가 올 때까지 기다리는 함수이다. <code>signal()</code>은 V함수라고도 하며, 공유 자원에 대한 접근을 처리한다.</p>
<p>세마포어에는 조건 변수가 없고 프로세스나 스레드가 세마포어 값을 수정할 때 다른 프로세스나 스레드는 동시에 세마포어 값을 수정할 수 없다.</p>
<h4 id="바이너리-세마포어">바이너리 세마포어</h4>
<blockquote>
</blockquote>
<p><code>0</code>과 <code>1</code>의 두 가지 값만 가질 수 있는 세마포어</p>
<p>구현의 유사성으로 인해 뮤택스는 바이너리 세마포어라고 할 수 있지만 뮤텍스는 잠금을 기반으로 상호배제가 일어나는 <code>잠금 매커니즘</code>으로 수행되며 <strong><em>세마포어는 신호를 기반으로 상호배제가 일어나는 <code>신호 매커니즘</code>으로 수행</em></strong>된다.</p>
<h4 id="카운팅-세마포어">카운팅 세마포어</h4>
<blockquote>
</blockquote>
<p>여러 개의 값을 가질 수 있는 세마포어</p>
<p>여러 자원에 대한 접근을 제어하는데 사용된다.</p>
<h4 id="모니터">모니터</h4>
<blockquote>
</blockquote>
<p>둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근할 수 있도록 공유 자원을 숨기고 해당 접근에 대해 인터페이스만을 제공하는 방법</p>
<p>공유 자원에 대한 작업들을 순차적으로 처리하며 세마포어보다 구현하기 쉽다는 장점이있다. 또한 모니터에서 상호 배제는 자동인 반면에, 세마포어에서는 상호 배제를 명시적으로 구현해야 하는 차이점이 있다.</p>
<hr>
<h1 id="7-교착-상태deadlock">7. 교착 상태(DeadLock)</h1>
<blockquote>
</blockquote>
<p><strong>교착상태(DeadLock)</strong>는 <strong><em>두 개 이상의 프로세스들이 서로가 가진 자원을 기다리며 중단된 상태</em></strong></p>
<p>교착 상태가 발생하는 원인과 이를 해결하기 위한 방법에 대해 알아보도록 하자🤔</p>
<h2 id="7-1-교착-상태의-원인">7-1. 교착 상태의 원인</h2>
<table>
<thead>
<tr>
<th>종류</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>상호 배제</strong></td>
<td>한 프로세스가 자원을 독점하고 있으며 다른 프로세스들은 접근이 불가능</td>
</tr>
<tr>
<td><strong>점유 대기</strong></td>
<td>특정 프로세스가 점유한 자원을 다른 프로세스가 요청하는 상태</td>
</tr>
<tr>
<td><strong>비선점</strong></td>
<td>다른 프로세스의 자원을 강제적으로 가져올 수 없음</td>
</tr>
<tr>
<td><strong>환형 대기</strong></td>
<td>프로세스 A는 프로세스 B의 자원을 요구하고, 프로세스 B는 프로세스 A의 자원을 요구하는 등 서로가 서로의 자원을 요구하는 상황</td>
</tr>
</tbody></table>
<h2 id="7-2-교착-상태의-해결-방법">7-2. 교착 상태의 해결 방법</h2>
<ul>
<li>자원을 할당할 때 애초에 조건이 성립되지 않도록 설계한다.</li>
<li>교착 상태 가능성이 없을 때만 자원이 할당되며, 프로세스당 요청할 자원들의 최대치를 통해 자원 할당 가능 여부를 파악하는 <code>은행원 알고리즘</code>을 사용한다.</li>
<li>교착 상태가 발생하면 사이클이 있는지 찾아보고 이에 관련된 프로세스르 한 개씩 지운다.</li>
<li>교착 상태는 매우드물게 일어나기 때문에 이를 처리하는 비용이 더 커서 교착 상태가 발생하면 사용자가 작업을 종료한다.
➡️ 현대 운영체제에서는 이방법을 채택하였다.</li>
</ul>
<blockquote>
</blockquote>
<p>📢 <strong>은행원 알고리즘</strong> : <strong>교착 상태(Deadlock)</strong>를 방지하기 위한 자원 할당 알고리즘</p>
<ul>
<li>자원을 할당할 때 시스템이 <strong>안정 상태(Safe State)</strong>에 있는지를 미리 계산하고, 시스템이 교착 상태에 빠지지 않도록 자원 할당을 관리한다. </li>
<li>이 알고리즘은 은행의 대출 시스템에서 유래되었으며 자원이 안전하게 할당되는지 확인하는 과정에서 은행원의 대출 관리 방식과 유사한 구조를 갖는 알고리즘이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제 - 메모리 : 메모리의 계층, 메모리 관리]]></title>
            <link>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-pjrotyb1</link>
            <guid>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-pjrotyb1</guid>
            <pubDate>Sun, 29 Sep 2024 08:26:19 GMT</pubDate>
            <description><![CDATA[<h1 id="2-메모리">2. 메모리</h1>
<p>CPU는 단지 메모리에 올라와 있는 프로그램들의 명령어들을 실행하는 역할을 수행한다. 그렇기 때문에 우리는 메모리에 대해서 조금 더 자세히 알 필요가 있다!! 😲
이번 포스트에서는 <strong>메모리가 어떠한 계층</strong>으로 구성이 되어있는지 또한 <strong>메모리를 어떻게 관리</strong>하는지에 대해서 알아보도록 하자!</p>
<h2 id="2-1-메모리의-계층memory-hierarchy">2-1. 메모리의 계층(Memory hierarchy)</h2>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/6d90f8bf-757d-4ce5-b0dc-3322d78707c9/image.jpg" alt=""></p>
<p>메모리의 계층은 다음과 같이 레지스터, 캐시, 주기억장치, 보조기억장치로 구성되어 있다. 
<strong><em>계층 위로 올라갈수록 가격은 비싸지지만 용량은 작아지고 속도가 빨라</em></strong>지며, <strong><em>계층 아래로 내려갈 수록 가격은 저렴해지고 용량은 커지는 반면에 속도가 느려</em></strong>지는 점이 있다.</p>
<h5 id="레지스터-캐시-주기억장치ram-전력이-공급되지-않으면-내용이-저장되지-않는-휘발성의-특성을-가지고-있지만-보조기적장치는-비휘발성의-특성을-가지고-있다">레지스터, 캐시, 주기억장치(RAM), 전력이 공급되지 않으면 내용이 저장되지 않는 휘발성의 특성을 가지고 있지만 보조기적장치는 비휘발성의 특성을 가지고 있다.</h5>
<hr>
<h3 id="캐시cache">캐시(Cache)</h3>
<blockquote>
</blockquote>
<p><strong>캐시(Cache)</strong>는 데이터를 미리 복사해 놓는 임시 저장소이자 빠른 장치와 느린장치에서의 속도 차이에 따른 병목 현상을 줄여주는 메모리</p>
<ul>
<li>데이터를 접근하는 시간이 오래 걸리는 경우를 해결해주며 무언가를 다시 계산하는 시간을 절약해준다.</li>
</ul>
<p>메모리와 CPU 사이의 속도 차이가 너무 크게 나기 때문에 중간에 레지스터 계층을 둬서 속도 차이를 해결하려고 한다. 속도 차이를 해결하기 위해 계층과 계층 사이에 있는 계층을 <strong><em>캐싱 계층</em></strong>이라고 한다. 예를 들어, 캐시 메모리와 보조기억장치 사이에 있는 주기억장치를 보조기억장치의 캐싱 계층이라고 한다.</p>
<h3 id="지역성의-원리locality">지역성의 원리(Locality)</h3>
<p>캐시 계층을 두는 것 말고 캐시를 직접 설정할 때는 자주 사용하는 데이터를 기반으로 설정하는데, 자주 사용하는 데이터를 구분할 때는 <strong><u>지역성</u></strong>을 기반으로 설정한다.</p>
<p>지역성(Locality)는 크게 <strong>시간 지역성(temporal locality)</strong>와 <strong>공간 지역성(spatial locality)</strong>로 나뉘어 진다.</p>
<ul>
<li><strong>시간 지역성(temporal locality)</strong>은 최근 사용한 데이터에 다시 접근하려는 특성을 의미한다.</li>
<li><strong>공간 지역성(spatial locality)</strong>은 최근 접근한 데이터를 이루고 있는 공간이나 그 가까운 공간에 접근하는 특성을 의미한다. </li>
</ul>
<hr>
<h3 id="캐시히트cache-hit와-캐시미스cache-miss">캐시히트(Cache Hit)와 캐시미스(Cache Miss)</h3>
<blockquote>
<p>캐시 메모리에서 원하는 데이터를 찾아오는 것을 <strong>캐시히트(Cache Hit)</strong>, 원하는 데이터를 찾이 못하여 주기억장치로 가서 데이터를 찾아오는 것을 <strong>캐시미스(Cache Miss)</strong>라고 한다.</p>
</blockquote>
<p>캐시히트를 하게 되면 해당 데이터를 제어장치를 거쳐서 가져오게 되는데 위치도 가깝고 CPU 내부 버스를 기반으로 작동하기 때문에 빠르다.
하지만 캐시미스가 발생하게 되면 메모리에서 가져오며 시스템 버스를 기반으로 작동하기 때문에 느리다.</p>
<hr>
<h3 id="캐시매핑cache-mapping">캐시매핑(Cache Mapping)</h3>
<blockquote>
</blockquote>
<p><strong>캐시매핑</strong>이란 캐시가 히트되기위해 매핑하는 방법 </p>
<ul>
<li>CPU의 레지스터와 주메모리(RAM)간에 데이터를 주고받을 때 기반으로 한다.</li>
</ul>
<h5 id="캐시매핑의-분류">캐시매핑의 분류</h5>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>직접 매핑(directed mapping)</td>
<td>메모리의 각 블록은 고정된 하나의 캐시 블록에만 저장될 수 있는 방식</td>
</tr>
<tr>
<td>연관 매핑(associate mapping)</td>
<td>메모리 블록이 캐시의 어느 블록에나 저장될 수 있는 방식</td>
</tr>
<tr>
<td>집합 연관 매핑(set associate mapping)</td>
<td>직접 매핑과 연관 매핑의 절충 방식으로, 캐시를 여러 <strong>세트(Set)</strong>로 나누고, 각 세트 내에서는 연관 매핑이 적용되는 방식</td>
</tr>
</tbody></table>
<hr>
<h3 id="웹-브라우저의-캐시">웹 브라우저의 캐시</h3>
<p>소프트웨어적인 대표적인 캐시가 있는데 바로 웹브라우저의 캐시이다.</p>
<p>웹 브라우저의 작은 저장소는 쿠키, 로컬 스토리지, 세션 스토리지가 있으며, 보통 사용자의 커스텀한 정보나 인증 모듈 관련 사항들을 웹 브라우저에 저장해서 추후 서버에 요청할 때 자신을 나타내는 식별자나 중복 요청 방지를 위해 쓰인다. 이러한 캐시는 오리진(origin)에 종속된다.</p>
<hr>
<blockquote>
<p>📢 <strong>웹 브라우저의 저장소</strong>는 _<u>쿠키, 로컬 스토리지, 세션 스토리지</u>_로 구성된다!</p>
</blockquote>
<p><strong>1. 쿠키(Cookie)</strong></p>
<blockquote>
</blockquote>
<p><strong>쿠키(Cookie)</strong>는 만료기한이 있는 Key-Value 저장소</p>
<ul>
<li>same site 옵션을 strict로 설정하지 않았을 경우 다른 도메인에서 요청했을 때 자동 전송되며, 4KB까지 데이터를 저장할 수 있고 만료기한을 정할 수 있다.</li>
<li>쿠키를 설정할 때는 document.cookie로 쿠키를 볼 수 없게 httponly 옵션을 거는 것이 중요하며, 서버에서 만료기한을 정한다.</li>
</ul>
<p><strong>2. 로컬 스토리지(Local Storage)</strong></p>
<blockquote>
</blockquote>
<p><strong>로컬 스토리지(Local Storage)</strong>는 만료기한이 없는 Key-Value 저장소</p>
<ul>
<li>5MB까지 저장할 수 있으며 <strong>웹 브라우저를 닫아도 유지</strong>가 된다.</li>
<li>HTML5를 지원하지 않는 웹 브라우저에서는 사용할 수 없다.</li>
<li>클라이언트에서만 수정이 가능하다.</li>
</ul>
<p><strong>3. 세션 스토리지(Session Storage)</strong></p>
<blockquote>
</blockquote>
<p><strong>세션 스토리지(Session Storage)</strong>는 만료기한이 없는 Key-Value 저장소</p>
<ul>
<li>탭 단위로 세션 스토리지를 생성하며, 탭을 닫을 때 해당 테이터가 삭제된다.</li>
<li>5MB까지 저장이 가능하며 HTML5를 지원하지 않는 웹 브라우저에서는 사용할 수 없다.</li>
<li>클라이언트에서만 수정이 가능하다.</li>
</ul>
<p><strong>4. Redis</strong></p>
<blockquote>
</blockquote>
<p>메인 데이터베이스 위에 <strong>레디스(Redis)</strong> 데이터베이스 계층을 캐싱 계층으로 둬서 성능을 향상시킨다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/5ca28eea-9384-4009-bb1e-c6ccf43424a2/image.jpg" alt=""></p>
<hr>
<h2 id="2-2-메모리의-관리memory-management">2-2. 메모리의 관리(Memory Management)</h2>
<p>메모리 관리는 한정된 물리적 메모리 자원을 효율적으로 사용하기 위해 다양한 기법들을 사용하는 중요한 운영체제 기능이며 메모리 관리의 핵심 기법으로는 _<u>가상 메모리(Virtual Memory), 메모리 할당(Memory Allocation), 그리고 페이지 교체 알고리즘(Page Replacement Algorithm)</u>_이 있다.</p>
<h3 id="1-가상-메모리virtual-memory">1. 가상 메모리(Virtual Memory)</h3>
<blockquote>
</blockquote>
<p><strong>가상 메모리(Virtual Memory)</strong>는 메모리 관리 기법의 하나로 컴퓨터가 실제로 이용 가능한 메모리 자원을 추상화하는 기법</p>
<ul>
<li>가상적으로 주어진 주소를 가상 주소(Logical Address)라고 하며, 실제 메모리 상에 잇는 주소를 실제 주소(Physical Address)라고 한다.</li>
<li>가상 주소는 메모리관리장치(MMU)에 의해 실제 주소로 변환이 되며, 메모리관리장치 덕분에 사용자는 실제 주소를 의식할 필요 없이 프로그램을 구축할 수 있게 된다.</li>
</ul>
<p>가상 메모리는 가상 주소와 실제 주소가 매핑되어 있고 프로세스의 주소 정보가 들어있는 <strong>&#39;페이지 테이블&#39;</strong>로 관리된다. 속도 향상을 위해서는 TLB를 사용한다.</p>
<blockquote>
<p>📢 <strong>TLB</strong>는 메모리와 CPU 사이에 있는 주소 변환을 위한 캐시이며 페에지 테이블에 있는 리스트를 보관하며 CPU가 페이지 테이블까지 가지 않도록 해 속도를 향상시킬 수 있는 캐시 계층</p>
</blockquote>
<p>가상 메모리에서 일어나는 현상에는 _<u>스와핑(Swapping)</u>_과 _<u>페이지 폴트(Page Fault)</u>_가 존재하는데 두 개념은 가상 메모리에서 메모리를 효율적으로 관리하고 물리 메모리(RAM)의 한계를 보완하기 위해 중요한 역할을 한다.</p>
<hr>
<h4 id="1-1-스와핑swapping">1-1. 스와핑(Swapping)</h4>
<blockquote>
</blockquote>
<p><strong>스와핑(Swapping)</strong>은 메모리에서 당장 사용하지 않는 영역을 하드 디스크로 옮기고 하드디스크의 일부분을 마치 메모리처럼 불러와 사용하는 것을 의미한다.</p>
<ul>
<li>페이지 폴트가 일어나지 않는 것처럼 만든다.</li>
</ul>
<p>여기서 말하는 <strong><em>페이지 폴트(Page Fault)</em></strong>라는 것이 무엇일까?🤔 
한번 알아보자 !!</p>
<hr>
<h4 id="1-2-페이지-폴트page-fault">1-2. 페이지 폴트(Page Fault)</h4>
<blockquote>
</blockquote>
<p><strong>페이지 폴트(Page Fault)</strong>는 프로세스의 주소 공간에는 존재하지만, 실제 물리적 메모리(RAM)에는 없는 데이터에 접근하려고 할 때 발생하는 현상</p>
<h4 id="페이지-폴트와-그로-인해-발생하는-스와핑의-과정">페이지 폴트와 그로 인해 발생하는 스와핑의 과정</h4>
<ol>
<li>어떤 명령어가 유효한 가상 주소에 접근했으나 해당 페이지가 만약 없다면 트랩(SW Interrupt)가 발생하게 되어 운영체제에게 알린다.</li>
<li>운영체제는 실제 디스크로부터 사용하지 않은 프레임을 찾는다.</li>
<li>해당 프레임을 실제 메모리에 가져와서 페이지 교체 알고리즘을기반으로 특정 페이지와 교체한다. ➡️ _<U>스와핑이 발생한다.</U>_</li>
<li>페이지 테이블을 갱신시킨 후 해당 명령어를 다시 시작한다.</li>
</ol>
<blockquote>
</blockquote>
<p>📢 <strong>페이지(Page)</strong>는 가상 메모리를 사용하는 최소 크기 단위, <strong>프레임(Frame)</strong>은 실제 메모리를 사용하는 최소 크기 단위</p>
<hr>
<h3 id="스레싱">스레싱</h3>
<blockquote>
</blockquote>
<p><strong>스레싱(Thrashing)</strong>은 _<U>메모리의 페이지 폴트율(Page Fault Rate)이 높은 것</U>_</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/58bb1a69-f327-4075-a087-f8f6a5e300c0/image.jpg" alt=""></p>
<ul>
<li>스레싱이 많이 발생하게 되면 컴퓨터의 심각한 성능 저하가 발생한다.</li>
<li>스레싱이 발생하는 이유는 페이지 폴트가 일어나면 CPU 이용률이 낮아지고, CPU 이용률이 낮아지면 운영체제가 CPU가 한가하다고 인식하여 가용성을 더 높이기 위해 메모리에 더 많은 프로세스를 올리게 된다.</li>
<li>이러한 현상이 반복되면 스레싱이 발생하게 되는 것이다.</li>
</ul>
<blockquote>
<p><strong>해결방안🤓</strong>
메모리(RAM)을 늘리든가, 보조기억장치가 HDD이면 SSD로 바꾸는 물리적으로 해결하는 방법과 운영체제에서 해결할 수 있는 작업 세트(Working Set)와 PFF가 있다.</p>
</blockquote>
<h4 id="작업-세트working-set">작업 세트(Working Set)</h4>
<blockquote>
</blockquote>
<p><strong>작업 세트</strong>는 프로세스의 과거 사용 이력인 지역성을 토앻 결정된 페이지 집합을 만들어서 미리 메모리에 로드하는 방법</p>
<ul>
<li>미리 로드하게 되면 탐색에 드는 비용을 줄일 수 있고 스와핑 또한 줄일 수 있다는 점이 있다.</li>
</ul>
<h4 id="pffpage-fault-frequency">PFF(Page Fault Frequency)</h4>
<blockquote>
</blockquote>
<p><strong>PFF</strong>는 이름 그대로 페이지 폴트 빈도를 조절하는 방법으로서 상한선과 하한선을 만드는 방법</p>
<ul>
<li>상한선에 도달하게 된다면 프레임을 늘리고 하한선을 도달하게 된다면 프레임을 줄이는 방법이다.</li>
</ul>
<hr>
<h3 id="2-메모리-할당memory-allocation">2. 메모리 할당(Memory Allocation)</h3>
<p>메모리에 프로그램을 할당할 때 시작 메모리의 위치와 메모리의 할당 크기를 기반으로 할당을 진행하는데 할당은 _<U>연속 할당(Contiguous Allocation)</U>_과 _<U>불연속 할당(Noncontiguous Allocation)</U>_으로 나뉜다.</p>
<h4 id="2-1-연속-할당contiguous-allocation">2-1. 연속 할당(Contiguous Allocation)</h4>
<blockquote>
</blockquote>
<p>메모리에 <strong>&#39;연속적으로&#39;</strong> 공간을 할당하는 것을 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/8b55f1f4-8dac-4888-8e94-cb4fa1e9ac32/image.jpg" alt=""></p>
<ul>
<li>연속 할당에는 메모리를 미리 나누어 관리하는 <strong><em>고정 분할 방식(Fixed Partition Allocation)</em></strong>과 매 시점 프로그램의 크기에 맞게 메모리를 분할하여 사용하는 <strong><em>가변 분할 방식(Variable Partition Allocation)</em></strong>이 있다.</li>
</ul>
<p><strong>고정 분할 방식</strong>은 메모리를 미리 나누어 관리하는 방식이기 때문에 융통성이 존재하지 않아 내부 단편화가 발생한다. 
<strong>가변 분할 방식</strong>은 매 시점 프로그램의 크기에 맞게 동적으로 메모리를 나눠 사용하기 때문에 내부 단편화가 발생하지 않지만 외부 단편화가 발생할 수 있다. ➡️ 가변 분할 방식에는 <strong>최초 척합(First Fit)</strong>, <strong>최적 적합(Best Fit)</strong>, <strong>최악 적합(Worst Fit)</strong>이 존재한다.</p>
<p><strong>가변 분할 방식 종류</strong></p>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>최초 적합(First Fit)</td>
<td>위쪽이나 아래쪽부터 시작해서 홀(할당할 수 있는 비어 있는 메모리 공간)을 찾으면 바로 할당</td>
</tr>
<tr>
<td>최적 적합(Best Fit)</td>
<td>프로세스의 크기 이상인 공간 중 가장 작은 홀부터 할당</td>
</tr>
<tr>
<td>최악 적합(Worst Fit)</td>
<td>프로세스의 크기와 가장 많이 차이가 나는 홀에 할당</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/kkong_do/post/a2ca91e4-ea6c-4b25-b5be-c12bceb27904/image.jpg" alt=""></td>
<td></td>
</tr>
</tbody></table>
<hr>
<h4 id="2-2-불연속-할당noncontiguous-allocation">2-2. 불연속 할당(Noncontiguous Allocation)</h4>
<p>메모리를 연속적으로 할당하지 않는 불연속 할당은 현대 OS에서 많이 쓰이는 방법인 <strong>페이징(Paging) 기법</strong>과 <strong>세그멘테이션(Segmentation) 기법</strong>, <strong>페이지드 세그멘테이션(Paged Segmentation) 기법</strong>으로 구성되어 있다.</p>
<h4 id="페이징paging-기법">페이징(Paging) 기법</h4>
<blockquote>
</blockquote>
<p>메모리를 고정된 크기의 페이지(Page) 단위로 나누고, 프로세스를 이 페이지 단위로 나누어 메모리의 서로 다른 위치에 할당하는 기법</p>
<p>페이징 기법은 외부 단편화를 해결하는 데 유리하며 메모리가 고정된 크기의 페이지로 나누어지기 때문에 프로세스는 어디든 빈 페이지 프레임에 할당될 수 있다. 또한, 효율적인 메모리를 사용할 수 있는데 페이지 크기가 동일하므로, 메모리 사용이 효율적이기 때문이다. 
하지만 가상 주소를 물리 주소로 변환하는 작업(페이지 테이블을 통한 주소 매핑)이 추가되어 주소 변환이 복잡해질 수 있다.</p>
<h4 id="세그멘테이션segmentaion-기법">세그멘테이션(Segmentaion) 기법</h4>
<blockquote>
</blockquote>
<p>메모리를 고정된 크기가 아닌, 의미 있는 단위로 나누어 할당하는 기법</p>
<ul>
<li><strong>세그먼트(Segment)</strong>는 논리적인 단위로, 코드, 데이터, 스택 등을 구분하여 메모리에 저장하는 단위이다.</li>
<li>코드와 데이터로 나누거나 코드 내의 작은 함수를 세그먼트로 놓고 나눌 수 있다. 이는 공유와 보안 측면에서 장점을 가지지만 홀 크기가 균일하지 않은 단점이 있다.</li>
<li>세그먼트 크기가 다르므로, 메모리 공간에 외부 단편화가 발생할 수 있다. 즉, 남는 메모리 공간이 프로세스의 세그먼트 크기와 맞지 않는 경우가 생길 수 있다(=외부 단편화 발생)</li>
</ul>
<h4 id="페이지드-세그멘테이션paged-segmentation-기법">페이지드 세그멘테이션(Paged Segmentation) 기법</h4>
<blockquote>
</blockquote>
<p>세그멘테이션과 페이징 기법을 결합한 방식으로, 프로그램을 <strong>의미 있는 단위(세그먼트)</strong>로 나눈 뒤, 각 세그먼트를 다시 고정된 크기의 페이지로 나누어 메모리에 할당하는 기법</p>
<p>세그먼트를 통해 코드, 데이터 등의 논리적인 단위를 유지하여 공유 및 보안 측면에서 장점을 유지할 수 있는 <strong><em>세그멘테이션의 장점</em></strong>과 고정된 크기의 페이지 단위로 나누어 외부 단편화 문제를 완화하고, 메모리 관리가 효율적인 <strong><em>페이징의 장점</em></strong>을 가지고 있다.</p>
<hr>
<h3 id="3-페이지-교체-알고리즘page-replacement-algorithm">3. 페이지 교체 알고리즘(Page Replacement Algorithm)</h3>
<blockquote>
<p>가상 메모리 시스템에서 <strong>페이지 폴트(Page Fault)</strong>가 발생할 때, <strong>물리적 메모리(RAM)</strong>가 꽉 차서 더 이상 페이지를 저장할 공간이 없을 경우, 어느 페이지를 교체할지 결정하는 알고리즘</p>
</blockquote>
<ul>
<li>페이지 교체 알고리즘은 페이지 폴트의 빈도를 최소화하여 메모리 사용을 최적화하고 성능을 향상시키는 것이다.</li>
</ul>
<h4 id="2-1-페이지-교체-알고리즘의-종류">2-1. 페이지 교체 알고리즘의 종류</h4>
<ul>
<li>오프라인 알고리즘(Offline Algorithm)</li>
<li>FIFO(Fisrt In First Out)</li>
<li>LRU(Least Recently Used)</li>
<li>NUR(Not Used Recently)</li>
<li>LFU(Least Frequently Used)</li>
</ul>
<p><strong>오프라인 알고리즘(Offline Algorithm)</strong>은 먼 미래에 참조되는 페이지와 현재 할당하는 페이지를 바꾸는 알고리즘으로, 가장 좋은 방법이지만 미래에 사용되는 프로세스를 알지 못하기 때문에 사용할 수 없다. 단지, 다른 알고리즘과의 성능 비교에 대한 상한 기준(Upper_Bound)를 제공해준다.</p>
<p><strong>FIFO(First In First Out)</strong>는 가장 먼저 온 페이지를 교체 영역에 가장 먼저 놓는 방식이며, 구현이 간단하다. 하지만 최근에 자주 사용된 페이지라도 먼저 들어왔다는 이유로 교체될 수 있어 비효율적일 수 있다. 페이지 프레임 수를 늘려도 페이지 폴트가 증가할 수 있는 문제인 Belady의 역설이 발생할 수 있다.</p>
<p><strong>LRU(Least Recently Used)</strong>는 참조가 가장 오래된 페이지를 바꾸는 방식, 각 페이지가 마지막으로 참조된 시간을 기록하고 가장 오랫동안 참조되지 않은 페이지를 교체하는 방식이며 오래된 것을 파악하기 위해 각 페이지 마다 계수기, 스택을 두어야 하는 점이 발생한다. LRU는 해시 테이블과 이중 연결 리스트로 구현한다.</p>
<p><strong>NUR(Not Used Recently)</strong>는 일명&#39;Clock 알고리즘, Second Chance 알고리즘&#39;이라고도 불리며 LRU에서 발전한 알고리즘으로써, LRU보다 구현이 더 간단하고 하드웨어 부담이 적다. 이러한 NUR은 <strong>참조 비트(Reference Bit)</strong>와 <strong>수정 비트(Modified Bit)</strong>를 사용하여 페이지 교체를 결정합니다.</p>
<p><strong>LFU(Least Frequently Used)</strong>가장 적게 사용된 페이지를 교체하는 방식으로써, 각 페이지가 얼마나 자주 참조되었는지 기록하고, 참조 횟수가 가장 적은 페이지를 교체한다. 이러한 LFU는 자주 사용되는 페이지는 오래 유지되므로 효율적일 수 있지만 참조 횟수를 기록하는 데 추가 비용이 들며, 오래된 페이지가 한 번 참조된 경우 자주 사용되지 않더라도 계속 남아 있을 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제 - 운영체제와 컴퓨터]]></title>
            <link>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%99%80-%EC%BB%B4%ED%93%A8%ED%84%B0-x8y2t0wa</link>
            <guid>https://velog.io/@kkong_do/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%99%80-%EC%BB%B4%ED%93%A8%ED%84%B0-x8y2t0wa</guid>
            <pubDate>Sat, 28 Sep 2024 06:08:17 GMT</pubDate>
            <description><![CDATA[<h2 id="1-운영체제와-컴퓨터">1. 운영체제와 컴퓨터</h2>
<blockquote>
</blockquote>
<p><strong>운영체제(OS, Operation System)</strong></p>
<ul>
<li>하드웨어와 소프트웨어를 관리하는 일종의 일꾼</li>
<li>사용자가 컴퓨터를 쉽게 다루게 해주는 인터페이스</li>
</ul>
<ul>
<li><strong>컴퓨터</strong> : 운영체제와 CPU, 메모리 등으로 이루어진 장치</li>
</ul>
<h2 id="1-1-운영체제의-역할과-구조">1-1. 운영체제의 역할과 구조</h2>
<h3 id="1-1-1-운영체제의-역할">1-1-1. 운영체제의 역할</h3>
<p>운영체제의 역할을 크게 네 가지로 나뉘어 진다.</p>
<table>
<thead>
<tr>
<th>종류</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CPU 스케줄링과 프로세스 관리</strong></td>
<td>CPU 소유권을 어떤 프로세스에 할당할지, 프로세스의 생성과 삭제, 자원 할당 및 반환을 관리</td>
</tr>
<tr>
<td><strong>메모리 관리</strong></td>
<td>한정된 메모리를 어떤 프로세스에 얼마큼 할당해야하는지 관리</td>
</tr>
<tr>
<td><strong>디스크 파일 관리</strong></td>
<td>디스크 파일을 어떠한 방법으로 보관할지 관리</td>
</tr>
<tr>
<td><strong>I/O 디바이스 관리</strong></td>
<td>I/O 디바이스들인 마우스, 키보드와 컴퓨터 칸에 데이터를 주고받는것을 관리</td>
</tr>
</tbody></table>
<h3 id="1-1-2-운영체제의-구조">1-1-2. 운영체제의 구조</h3>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/568acc63-2ef8-4bee-b646-32513aa7a179/image.jpg" alt=""></p>
<ul>
<li>그림과 같이 소프트웨어(유저 프로그램)가 맨 위에 있으며, GUI, 시스템콜, 커널, 드라이버가 있으며 가장 밑에는 하드웨어가 있는 구조로 이루어져 있다.</li>
<li><strong>운영체제는 GUI, 시스템콜, 커널, 드라이버를 포함</strong>해서 이루어져 있으며 GUI가 없는 서버(Ex. Linux)도 있다.</li>
</ul>
<h4 id="📖-용어-정리">📖 용어 정리</h4>
<blockquote>
</blockquote>
<p><strong>GUI</strong>는단순 명령어 창이 아닌 아이콘을 마우스로 클릭하는 단순한 동작으로 컴퓨터와 상호작용할 수 있도록 도와주는 사용자 인터페이스의 한 형태
<strong>CUI</strong>는 앞서 말한 GUI와 반대로 그래픽이 아닌 명령어로 컴퓨터와 상호작용할 수 있도록 도와주는 인터페이스</p>
<blockquote>
</blockquote>
<p><strong>쉘(Shell)</strong>은 사용자가 운영체제에 명령어를 입력하는 인터페이스이고, 사용자로부터 명령어를 입력받아 이를 시스템 콜을 통해 커널에 전달하는 역할을 한다.
<strong>커널(Kernel)</strong>은  하드웨어와 응용 프로그램 간의 상호작용을 관리하며 사용자가 직접 접근할 수 없습니다.</p>
<blockquote>
</blockquote>
<p><strong>드라이버</strong>는 하드웨어를 제어하기 위한 소프트웨어</p>
<h4 id="1-1-2-1-시스템콜이란">1-1-2-1 <strong>시스템콜</strong>이란?</h4>
<p><strong><em>&#39;운영체제가 커널에 접근하기 위한 인터페이스&#39;</em></strong>를 의미하며, SW(유저 프로그램)가 운영체제의 서비스를 받기 위해 커널 함수를 호출할 때 사용하는 인터페이스이다.</p>
<ul>
<li><p>프로세스(Process)나 스레드(Thread)에서 운영체제로 어떠한 요청을 할 때 &#39;<strong>시스템 콜</strong>&#39;이라는 인터페이스와 커널을 거쳐서 운영체제에 전달하게 된다.</p>
</li>
<li><p>시스템 콜은 하나의 추상화된 계층이기 떄문에, 네트워크 통신이나 데이터베이스에서의 처리에 대한 부분을 신경 쓰지 않고 프로그램을 구현할 수 있다는 장점을 가지고 있다.</p>
</li>
</ul>
<p><strong>정리</strong>❗
SW(유저 프로그램)는 I/O 요청 처리하기 위해서 운영체제의 서비스를 받아야 하며 이러한 서비스는 커널을 통해서만 받을수 있다. 그렇기에 시스템 콜을 통해 커널에게 부탁하고 커널이 운영체제에게 전달하여 서비스를 처리한다.</p>
<p>➡️ 유저 모드와 커널 모드를 변환하면서 요청들을 처리하는데, 모드를 변환하기 위해서 <strong><em>modebit</em></strong>이라는 플래그 변수(flag bit)를 사용한다. 
시스템 콜을 호출하고 modebit을 통해서 모드를 바꾸면서 관련된 로직을 수행한다. </p>
<hr>
<h2 id="1-2-컴퓨터의-요소">1-2. 컴퓨터의 요소</h2>
<blockquote>
</blockquote>
<p><strong>컴퓨터(Computer)</strong> =  <strong><em>CPU, DMA 컨트롤러, 메모리, 타이머, 디바이스 컨트롤러 등으로 이루어진 장치</em></strong></p>
<p>컴퓨터는 크게 CPU, DMA 컨트롤러, 메모리, 타이머, 디바이스 컨트롤러 등으로 이루어져 있으며, 각 요소들이 무엇을 의미하는지, 어떠한 역할을 하는지에 대해 차근히 알아보도록 하자!</p>
<h3 id="1-2-1-cpu">1-2-1. CPU</h3>
<blockquote>
<p><strong>CPU</strong>(Central Processing Unit) = 산술논리연산장치(ALU), 제어장치(CU), 레지스터(R)로 이루어진 컴퓨터 장치</p>
</blockquote>
<ul>
<li>인터럽트(Interrupt)에 의해 단순히 메모리에 존재하는 명령어를 해석하여 수행하는 장치(일꾼)을 의미한다.</li>
<li>관리자의 역할을 하는 운영체제의 커널이 프로그램을 메모리(RAM)에 올려 프로세스로 만들면 일꾼인 CPU가 처리한다.</li>
</ul>
<h5 id="📢-인터럽트interrupt--프로세서가-실행-중인-작업을-잠시-멈추고-즉각-처리해야-할-중요한-이벤트가-발생했을-때-이를-처리하기-위해-호출되는-신호">📢 인터럽트(Interrupt) : 프로세서가 실행 중인 작업을 잠시 멈추고, 즉각 처리해야 할 중요한 이벤트가 발생했을 때 이를 처리하기 위해 호출되는 신호</h5>
<p><strong>산술논리연산장치(ALU)</strong></p>
<blockquote>
</blockquote>
<p><strong>산술논리연산장치(Arithmetic Logic Unit)</strong>는 _<u>산술 연산</u>_과 _<u>논리 연산</u>_을 계산하는 디지털 회로로서의 CPU의 한 부품</p>
<ul>
<li><strong><em>산술 연산</em></strong>은 덧셈, 뺄셈 같은 두 숫자의 연산을 의미하며, <strong><em>논리 연산</em></strong>은 배타적 논리합(XOR), 배타적 논리곱(XAND)와 같은 연산을 의미한다. </li>
</ul>
<p><strong>제어장치(CU)</strong></p>
<blockquote>
</blockquote>
<p><strong>제어장치(Control Unit)</strong>는 _<u>프로세스 조작을 지시</u>_하는 CPU의 한 부품</p>
<ul>
<li>입출력(I/O) 장치 간 통신을 제어하고 명령어들을 읽고 해석하며 데이터 처리를 위한 순서를 결정한다.</li>
</ul>
<p><strong>레지스터(R)</strong></p>
<blockquote>
</blockquote>
<p><strong>레지스터(Register)</strong>는 _<u>CPU안에 있는 임시기억장치</u>_의 역할을 하는 CPU의 한 부품 </p>
<ul>
<li>CPU와 직접 연결 되어있기 때문에 연산 속도가 메모리보다 수십~수백 배까지 빠른 장점이 있다.</li>
<li>레지스터를 거쳐서 CPU에 데이터가 전달된다.</li>
<li>레지스터의 종류도 굉장히 여러가지<del>(범용 레지스터, 인덱스 레지스터, PC 등등..)</del>이지만 해당 내용들을 다루기에는 너무 깊어지므로 여기까지만 말하고 넘어가겠다.</li>
</ul>
<h4 id="cpu의-연산-처리-과정">CPU의 연산 처리 과정</h4>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/f5a50b7d-6181-4522-a927-b1e2f8919880/image.jpg" alt=""></p>
<hr>
<p><strong>잠깐!!!🙉</strong> 
앞서 인터럽트(Interrupt)에 대해 짧게 말하고 넘어갔는데 중요한 내용이기 때문에 조금 더 알아보고 넘어가보도록 하자!!</p>
<p><strong>인터럽트(Interrupt)</strong></p>
<blockquote>
</blockquote>
<p>프로세서가 실행 중인 작업을 잠시 멈추고, 즉각 처리해야 할 중요한 이벤트가 발생했을 때 이를 처리하기 위해 호출되는 신호</p>
<ul>
<li>간단히 말하면 어떤 신호가 들어왔을 떄 CPU를 잠깐 정지시키는 것을 의마한다.</li>
<li>인터럽트의 종류에는 IO 디바이스로 인한 인터럽트와 0으로 숫자를 나누는 산술연산에서의 인터럽트, 프로세스 오류 등으로 일어나는 인터럽트가 있다.</li>
</ul>
<p>인터럽트가 발생하면 CPU는 현재 작업을 중단하고 <strong>인터럽트 핸들러 함수(Interrupt Service Routine, ISR)</strong>를 실행해야 하며 이를 위해 <strong>인터럽트 벡터(Interrupt Vector)</strong>를 참조해서 실행하게 된다.</p>
<p>이러한 인터럽트는 크게 두 가지의 종류로 나뉘어 지는데, <strong><em>하드웨어 인터럽트(Hardware Interrupt)</em></strong>와 <strong><em>소프트웨어 인터럽트(Software Interrupt)</em></strong> 두 가지로 나뉘어진다. <strong>그럼 인터럽트의 종류에 대해 알아보자</strong></p>
<p><strong>하드웨어 인터럽트(Hardware Interrupt)</strong></p>
<blockquote>
<p>키보드를 연결한다거나 마우스를 연결하는 일 등의 <strong>I/O 디바이스에서 발생하는 인터럽트</strong></p>
</blockquote>
<p>인터럽트 라인이 설계된 이후 순차적인 인터럽트 실행을 중지하고 운영체제에 시스템콜을 요청해서 원하는 디바이스로 향해 디바이스에 있는작은 로컬 버퍼에 접근하여 일을 수행한다.</p>
<p><strong>소프트웨어 인터럽트(Software Interrupt)</strong></p>
<blockquote>
<p>트랩(trap)이라고도 불리며, 프로세스 오류 등으로 <strong>프로세스가 시스템콜을 호출할 때 발동하는 인터럽트</strong></p>
</blockquote>
<hr>
<h3 id="1-2-2-dma-컨트롤러dma-controller">1-2-2. DMA 컨트롤러(DMA Controller)</h3>
<blockquote>
<p><strong>DMA 컨트롤러</strong>는 _<u>CPU의 개입 없이</u>_ 메모리와 주변 장치(하드 디스크, 네트워크 카드 등) 간에 _<u>데이터를 직접 전송</u>_할 수 있도록 하는 하드웨어 장치</p>
</blockquote>
<ul>
<li>CPU 부하를 막아주며 CPU의 일을 부담하는 보조 일꾼의 역할을 수행한다.</li>
<li>하나의 작업을 CPU와 DMA 컨트롤러가 동시에 하는 것을 방지한다.</li>
<li>대용량 데이터 전송 시 CPU의 부하를 줄이고 시스템 효율을 높이기 위해 사용된다.</li>
</ul>
<hr>
<h3 id="1-2-3-메모리memory">1-2-3. 메모리(Memory)</h3>
<blockquote>
</blockquote>
<p><strong>메모리(Memory)</strong>는 컴퓨터 시스템에서 데이터를 저장하고 이를 처리하기 위한 공간으로, 프로그램의 명령어, 데이터, 그리고 작업의 중간 결과를 저장하는 기억 장치</p>
<ul>
<li>일반적으로 메모리라 하면 <strong>RAM(Random Access Memory)</strong>을 가리키며, 이 메모리는 데이터를 임시적으로 저장하는 역할을 한다.</li>
<li>RAM은 전원이 꺼지면 저장된 데이터가 사라지기 때문에 <strong><em>휘발성 메모리</em></strong>라고도 한다.</li>
<li>메모리는 CPU가 명령어를 처리하고 계산할 때 필요한 데이터를 빠르게 저장하고 꺼내 쓰는 역할을 하며 CPU가 계산을 담당한다면, 메모리는 그 계산을 위한 데이터를 기억하는 역할을 수행한다.</li>
</ul>
<hr>
<h3 id="1-2-4-타이머timer">1-2-4. 타이머(Timer)</h3>
<blockquote>
</blockquote>
<p><strong>타이머(Timer)</strong>는 컴퓨터 시스템에서 일정한 시간 간격을 측정하거나 특정 시간에 맞춰 작업을 수행할 수 있도록 해주는 하드웨어 장치</p>
<ul>
<li>주로 시간 기반의 작업을 관리하고, 프로세스가 일정 시간 내에 작업을 끝내지 못하면 인터럽트를 발생시켜 시스템이 적절한 조치를 취하도록 돕는다.</li>
</ul>
<hr>
<h3 id="1-2-5-디바이스-컨트롤러divice-controller">1-2-5. 디바이스 컨트롤러(Divice Controller)</h3>
<blockquote>
</blockquote>
<p><strong>디바이스 컨트롤러(Divcie Controller)</strong>는 입출력 장치와 컴퓨터 시스템 간의 데이터를 관리하고 제어하는 하드웨어 장치 </p>
<ul>
<li>로컬 버퍼를 사용하여 데이터를 임시 저장하고, CPU와의 통신을 통해 입출력 작업을 수행한다.</li>
<li>입출력 작업이 완료되면 인터럽트를 발생시켜 CPU에게 알린다.</li>
</ul>
<p>이렇게 운영체제의 역할과 구조에 대해 전반적으로 살펴보았다! 
앞서 컴퓨터의 구성 요소들에 알아보았는데 다음으로 <strong>메모리</strong>에 대해 조금 더 깊숙히 알아보는 시간을 가지도록 하겠다!  </p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱 JWT - Login/Logout]]></title>
            <link>https://velog.io/@kkong_do/Spring-JWT-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kkong_do/Spring-JWT-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 10 Jul 2024 11:59:57 GMT</pubDate>
            <description><![CDATA[<h2 id="토큰-기반-인증이란">토큰 기반 인증이란?</h2>
<blockquote>
</blockquote>
<p><strong>토큰</strong>(token)은 서버에서 클라이언트를 구분하기 위한 유일한 값이며 서버가 토큰을 생성해서 클라이언트에게 제공하면, 클라이언트는 이 토큰을 갖고 있다가 여러 요청을 이 토큰과 함께 신청한다.
서버는 토큰만 보고 유효한 사용자인지 검증하는 것을 <strong><em>토큰 기반 인증</em></strong>이라고 한다.</p>
<h3 id="토큰을-전달하고-인증받는과정">토큰을 전달하고 인증받는과정</h3>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/ab216f1d-78ad-4e69-8eae-8e8e8d4576c8/image.png" alt=""></p>
<ol>
<li>클라이언트가 ID와 Password를 서버에게 전달하면서 인증을 요청한다.</li>
<li>서버는 ID와 Password를 확인해 유효한 사용자인지 검증한다. 만약 유효한 사용자면 토큰(Access token, Refresh token)을 생성하여 응답한다.</li>
<li>클라이언트는 서버에서 준 토큰을 저장한다.</li>
<li>이후 인증이 필요한 API를 사용할 때 토큰(Access token)을 함께 보낸다.</li>
<li>서버는 토큰이 유효한지 검증한다.</li>
<li>토큰이 유효한다면 클라이언트가 요청한 내용을 처리한다.</li>
</ol>
<h3 id="토큰-기반-인증의-특징">토큰 기반 인증의 특징</h3>
<ul>
<li>** 무상태성** </li>
<li>** 확장성 **</li>
<li>** 무결성 **</li>
</ul>
<hr>
<h2 id="jwt">JWT</h2>
<blockquote>
</blockquote>
<p><code>aaaaa(헤더) . bbbbb(내용) . cccccc(서명)</code></p>
<ul>
<li><code>.</code> 기준으로 <strong><em>헤더(header)</em></strong>, <strong><em>내용(payload)</em></strong>, <strong><em>서명(signature)</em></strong>으로 이루어져 있다.</li>
</ul>
<hr>
<h3 id="헤더header">헤더(Header)</h3>
<ul>
<li>헤더(Header)는 토큰의 타입과 해싱 알고리즘을 지정하는 정보를 담는다.</li>
<li>JWT 토큰, HS256 해싱 알고리즘을 사용한다는 내용이다.<pre><code class="language-json">// 토큰 타입과 해싱 알고리즘 예
{
   &quot;typ&quot; : &quot;JWT&quot;,
  &quot;alg&quot; : &quot;HS256&quot; 
}</code></pre>
</li>
</ul>
<h4 id="헤더header의-구성">헤더(Header)의 구성</h4>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>typ</td>
<td>토큰의 타입을 지정한다. JWT라는 문자열이 들어가게 된다.</td>
</tr>
<tr>
<td>alg</td>
<td>해싱 알고리즘을 지정한다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="내용payload">내용(Payload)</h3>
<ul>
<li>토큰과 관련된 정보를 담는다.</li>
<li>내용의 한 덩어리를 <code>클레임(Claim)</code>이라고 부르며, 클레임은 키값의 한 쌍으로 이루어져 있다.</li>
</ul>
<h4 id="클레임claim">클레임(Claim)</h4>
<ul>
<li>등록된 클레임, 공개 클레임, 비공개 클레임으로 나눈다.</li>
</ul>
<h4 id="등록된-클레임registered-claim">등록된 클레임(Registered claim)</h4>
<blockquote>
</blockquote>
<p><strong>등록된 클레임</strong>은 토큰에 대한 정보를 담는데 사용한다.</p>
<table>
<thead>
<tr>
<th align="center">이름</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">iss</td>
<td align="left">토큰 발급자(issue)</td>
</tr>
<tr>
<td align="center">sub</td>
<td align="left">토큰 제목(subject)</td>
</tr>
<tr>
<td align="center">aud</td>
<td align="left">토큰 대상자(audience)</td>
</tr>
<tr>
<td align="center">exp</td>
<td align="left">토큰의 만료 시간(expiration), 시간은 NumericDate 형식으로 하며, 항상 현재 시간 이후로 설정한다.</td>
</tr>
<tr>
<td align="center">nbf</td>
<td align="left">토큰의 활성 날짜와 비슷한 개념으로, nbf는 Not Before를 의미한다. NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다.</td>
</tr>
<tr>
<td align="center">iat</td>
<td align="left">토큰이 발급된 시간으로 iat은 issued at을 의미한다.</td>
</tr>
<tr>
<td align="center">jti</td>
<td align="left">jwt의 고유 식별자로서 주로 일회용 토큰에 사용한다.</td>
</tr>
</tbody></table>
<h4 id="공개-클레임public-claim">공개 클레임(public claim)</h4>
<blockquote>
</blockquote>
<p>** 공개 클레임 **은 공개되어도 상관없는 클레임을 의미한다.</p>
<ul>
<li>충돌을 방지할 수 있는 이름을 가져야 한다.</li>
<li>보통 클레임 이름을 URI로 짓는다.</li>
</ul>
<h4 id="비공개-클레임private-claim">비공개 클레임(private claim)</h4>
<blockquote>
</blockquote>
<p>** 비공개 클레임 **은 공개되면 안되는 클레임을 의미한다.</p>
<ul>
<li>클라이언트와 서버 간의 통신에 사용된다.</li>
</ul>
<pre><code class="language-json">{
    &quot;iss&quot; : &quot;kkongdo@gmail.com&quot;, // 등록된 클레임
      &quot;iat&quot; : 1622370878, // 등록된 클레임
    &quot;exp&quot; : 1622372678, // 등록된 클레임
    &quot;https://kkongdo.com/jwt_claims/is_admin&quot; : true,
      &quot;email&quot; : &quot;kkongdo@gmail.com&quot;,
    &quot;hello&quot; : &quot;안녕하세요!&quot;
}</code></pre>
<p> iss, iat, exp는 JWT 자체에서 등록된 클레임이고, URL로 네이밍된 <a href="https://kkongdo.com/jwt_claims/is_admin%EC%9D%80">https://kkongdo.com/jwt_claims/is_admin은</a> 공개 클레임이다. 그 외에 등록된 클레임도, 공개 클레임도 아닌 email과 hello는 비공개 클레임 값이다.</p>
<hr>
<h3 id="서명signature">서명(Signature)</h3>
<blockquote>
</blockquote>
<p><strong>서명(Signature)</strong>는 해당 토큰이 조작되었거나 변경되지 않았음을 확인하는 용도로 사용하며, 헤더(Header)의 인코딩 값과 내용(Payload)의 인코팅값을 합친 후에 주어진 비밀키를 사용해서 해시값을 생성한다.</p>
<hr>
<h2 id="리프레쉬-토큰refresh-token">리프레쉬 토큰(Refresh token)</h2>
<blockquote>
</blockquote>
<p><strong>리프레쉬 토큰(Refresh token)</strong>은 사용자를 인증하기 위한 용도가 아닌 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급하기 위해 사용하는 토큰이다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/40cc9418-c384-4d37-9068-f7d1dd2d6835/image.png" alt=""></p>
<h3 id="다음-절차를-차근히-따라와-보자🦾">다음 절차를 차근히 따라와 보자🦾</h3>
<ul>
<li><strong>1번</strong> : 클라이언트가 서버에게 인증을 요청한다.</li>
<li><strong>2번</strong> : 서버는 클라이언트에서 전달한 정보를 바탕으로 인증 정보가 유효한지 확인한 뒤, 액세스 토큰과 리프레시 토큰을 만들어서 클라이언트에게 전달한다. 전달받은 클라이언트는 저장한다.</li>
<li><strong>3번</strong> : 서버에서 생성한 리프레시 토큰은 DB에도 저장해둔다.</li>
<li><strong>4번</strong> : 인증을 필요로 하는 API를 호출할 때 클라이언트에 저장된 액세스 토큰과 함께 API를 요청한다.</li>
<li><strong>5번</strong> : 서버는 전달받은 액세스 토큰이 유효한지 검사한 뒤에 유효하다면 클라이언트에서 요청한 내용을 처리한다.</li>
<li><strong>6번</strong> : 시간이 지나고 액세스 토큰이 만료된 뒤에는 클라리언트에서 원하는 정보를 얻기 위해 서버에게 API 요청을 보낸다.</li>
<li><strong>7번</strong> : 서버는 액세스 토큰이 유효한지 검사한다. 만약 만료된 토큰이면 유효하지 않기 때문에 토큰이 만료되었다는 에러를 전달한다.</li>
<li><strong>8번</strong> : 클라리언트는 이 응답을 받고 저장해둔 리프레시 토큰과 함께 새로운 액세스 토큰을 발급하는 요청을 전송한다.</li>
<li><strong>9번</strong> : 서버는 전달받은 리프레시 토큰이 유효한지, DB에서 리프세리 토큰을 조회한 후 저장해둔 리프레시 토큰과 같은지 확인한다.</li>
<li><strong>10번</strong> : 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성한 뒤에 응답한다. 그 이후 클라이언트는 4번과 같이 다시 API를 요청한다.</li>
</ul>
<hr>
<h2 id="구현-방법">구현 방법</h2>
<h3 id="1-의존성-추가하기-buildgradle">1. 의존성 추가하기 (build.gradle)</h3>
<pre><code class="language-java">    // jwt 설정하기 위한 dependencies
    implementation &#39;io.jsonwebtoken:jjwt:0.9.1&#39;
    implementation &#39;io.jsonwebtoken:jjwt-api:0.11.5&#39;
    runtimeOnly &#39;io.jsonwebtoken:jjwt-impl:0.11.5&#39;
    runtimeOnly &#39;io.jsonwebtoken:jjwt-jackson:0.11.5&#39;
    runtimeOnly &#39;io.jsonwebtoken:jjwt-gson:0.11.5&#39;</code></pre>
<h3 id="2-토큰-제공자-추가하기">2. 토큰 제공자 추가하기</h3>
<h4 id="2-1-applicationproperties에-코드-추가하기">2-1. application.properties에 코드 추가하기</h4>
<pre><code class="language-java">jwt.issuer=kkongdo@gmail.com
jwt.secret_key=study-springboot</code></pre>
<h4 id="2-2-jwtproperties-생성-해당-값들을-변수로-접근하는데-사용">2-2 JwtProperties 생성 (해당 값들을 변수로 접근하는데 사용)</h4>
<pre><code class="language-java">package com.springboot.springsecurity.config.jwt;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Setter
@Getter
@Component
@ConfigurationProperties(&quot;jwt&quot;) // 자바 클래스에 프로퍼티값을 가져와서 사용하는 어노테이션이다.
public class JwtProperties {
    private String issuer; // kkongdo@gmail.com이 들어간다.
    private String secretKey; // study-springboot가 들어간다.
}</code></pre>
<h4 id="2-3-tokenprovider-생성-토큰을-생성하고-올바른-토큰인지-유효성-검사-및-토큰에서-필요한-정보-추출">2-3 TokenProvider 생성 (토큰을 생성하고 올바른 토큰인지 유효성 검사 및 토큰에서 필요한 정보 추출)</h4>
<pre><code class="language-java">package com.springboot.springsecurity.config.jwt;

import io.jsonwebtoken.*;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import com.springboot.springsecurity.domain.User;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Collections;
import java.util.Date;
import java.util.Set;

// 토큰을 생성하고 올바른 토큰인지 유효성 검사를 하고, 토큰에서 필요한 정보를 가져오는 클래스
@RequiredArgsConstructor
@Service
public class TokenProvider {
    private final JwtProperties jwtProperties;

    public String generateToken(User user, Duration expiredAt){
        Date now = new Date();
        return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);
    }

    // 1. JWT 토큰 생성 메서드
    private String makeToken(Date expiry, User user){
        Date now = new Date();

        return Jwts.builder()
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setIssuer(jwtProperties.getIssuer()) // 발급자
                .setIssuedAt(now) //발급날짜
                .setExpiration(expiry) // 만료일
                .setSubject(user.getEmail()) // 토큰 제목
                .claim(&quot;id&quot;, user.getId()) // 클레임에 유저 id저장
                .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
                .compact();
    }
    // 2. JWT 토큰 유효성 검증 메서드
    public boolean validToken(String token){
        try{
            Jwts.parser()
                    .setSigningKey(jwtProperties.getSecretKey()) //비밀키로 복호화
                    .parseClaimsJws(token);
            return true;
        }catch (Exception e){
            return false;
        }
    }
    // 3. 토큰 기반으로 인증 정보를 가져오는 메서드
    public Authentication getAuthentication(String token){
        Claims claims = getClaims(token);
        Set&lt;SimpleGrantedAuthority&gt; authorities = Collections.singleton(new SimpleGrantedAuthority(&quot;ROLE_USER&quot;));
        return new UsernamePasswordAuthenticationToken(new org.springframework.security.core.userdetails.User(
                claims.getSubject(), &quot;&quot;, authorities),token, authorities);
    }

    // 4. 토큰 기반으로 유저 ID를 가져오는 메서드
    public Long getUserId(String token){
        Claims claims = getClaims(token);
        return claims.get(&quot;id&quot;,Long.class);
    }

    private Claims getClaims(String token) {
        return Jwts.parser() // 클레임 조회
                .setSigningKey(jwtProperties.getSecretKey())
                .parseClaimsJws(token)
                .getBody();
    }


}
</code></pre>
<h3 id="3-리프레쉬-토큰-도메인-생성">3. 리프레쉬 토큰 도메인 생성</h3>
<pre><code class="language-java">package com.springboot.springsecurity.domain;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RefreshToken {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Long id;
    @Column(name = &quot;user_id&quot;, nullable = false, unique = true)
    private Long userId;
    @Column(name = &quot;refresh_token&quot;, nullable = false)
    private String refreshToken;

    public RefreshToken(Long userId, String refreshToken){
        this.userId = userId;
        this.refreshToken = refreshToken;
    }
    public RefreshToken update(String newRefreshToken){
        this.refreshToken = newRefreshToken;
        return this;
    }
}
</code></pre>
<h3 id="4-토큰-필터-생성">4. 토큰 필터 생성</h3>
<pre><code class="language-java">package com.springboot.springsecurity.config;

import com.springboot.springsecurity.config.jwt.TokenProvider;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    private final TokenProvider tokenProvider;
    private final static String HEADER_AUTHORIZATION = &quot;Authorization&quot;;
    private final static String TOKEN_PREFIX = &quot;Bearer&quot;;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 요청 헤더의 Authorization 키와 값을 조회한다.
        String authorizationHeader = request.getHeader(HEADER_AUTHORIZATION);
        // 가져온 값에서 접두사 제거한다.
        String token = getAccessToken(authorizationHeader);
        // 가져온 토큰이 유효한지 확인 및 유효한 때는 인증 정보 설정을 한다.
        if(tokenProvider.validToken(token)){
            Authentication authentication = tokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }

    private String getAccessToken(String authorizationHeader) {
        if(authorizationHeader != null &amp;&amp; authorizationHeader.startsWith(TOKEN_PREFIX)){
            return authorizationHeader.substring(TOKEN_PREFIX.length());
        }
        return null;
    }
}
</code></pre>
<h3 id="5-토큰-api-구현">5. 토큰 API 구현</h3>
<ul>
<li>리프레쉬 토큰을 전달받아 검증하고, 유효한 리프세시 토큰이라면 새로운 액세스 토큰을 생성하는 토큰 API를 구현해보겠다.</li>
</ul>
<h4 id="5-1-userservice에-유저-id로-유저를-검색해서-전달하는-findbyid-메서드-추가">5-1. UserService에 유저 ID로 유저를 검색해서 전달하는 findById() 메서드 추가</h4>
<pre><code class="language-java">package com.springboot.springsecurity.service;

import com.springboot.springsecurity.domain.User;
import com.springboot.springsecurity.dto.AddUserRequest;
import com.springboot.springsecurity.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public Long save(AddUserRequest dto) {
        return userRepository.save(User.builder()
                .email(dto.getEmail())
                // 패스워드 암호화
                .password(bCryptPasswordEncoder.encode(dto.getPassword()))
                .build()).getId();
    }

    public User findById(Long userId) {
        return userRepository.findById(userId).orElseThrow(() -&gt; new IllegalArgumentException(&quot;UnExpected user&quot;));
    }
}</code></pre>
<h4 id="5-2-refreshtokenservice-생성">5-2. RefreshTokenService 생성</h4>
<ul>
<li>전달받은 리프레시 토큰으로 리프레시 토큰 객체를 검색해서 전달하는 메서드를 만든다.<pre><code class="language-java">package com.springboot.springsecurity.service;
</code></pre>
</li>
</ul>
<p>import com.springboot.springsecurity.domain.RefreshToken;
import com.springboot.springsecurity.repository.RefreshTokenRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;</p>
<p>@Service
@RequiredArgsConstructor
public class RefreshTokenService {
    private final RefreshTokenRepository refreshTokenRepository;</p>
<pre><code>public RefreshToken findByRefreshToken(String refreshToken) {
    return refreshTokenRepository.findByRefreshToken(refreshToken).orElseThrow(() -&gt; new IllegalArgumentException(&quot;Unexpected toekn&quot;));
}</code></pre><p>}</p>
<pre><code>#### 5-3. TokenService 생성
- createNewAccessToken() 메서드를 만들어서 전달받은 리프레시 토큰으로 토큰 유효성을 검사하고 유효한 토큰일 때는 리프레시 토큰으로 사용자 ID를 찾는다.
- 이후 사용자 ID로 사용자를 찾은 후에 토큰 제공자 generateToken() 메서드를 호출해서 새로운 액세스 토큰을 생성한다.

```java
package com.springboot.springsecurity.service;

import com.springboot.springsecurity.config.jwt.TokenProvider;
import com.springboot.springsecurity.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.Duration;

@Service
@RequiredArgsConstructor
public class TokenService {
    private final TokenProvider tokenProvider;
    private final RefreshTokenService refreshTokenService;
    private final UserService userService;

    public String createNewAccessToken(String refreshToken) throws IllegalAccessException {
        // 토큰 유효성 검사에 실패하면 예외 발생
        if(!tokenProvider.validToken(refreshToken)){
            throw new IllegalAccessException(&quot;Unexpected token&quot;);
        }
        Long userId = refreshTokenService.findByRefreshToken(refreshToken).getUserId();
        User user = userService.findById(userId);

        return  tokenProvider.generateToken(user, Duration.ofHours(2));
    }
}</code></pre><h3 id="6-토큰을-발급받는-api-생성">6. 토큰을 발급받는 API 생성</h3>
<h4 id="6-1-토큰-생성-요청-createaccesstokenrequest와-토큰-생성-응답-createaccesstokenresponse를-만든다">6-1. 토큰 생성 요청 CreateAccessTokenRequest와 토큰 생성 응답 CreateAccessTokenResponse를 만든다.</h4>
<pre><code class="language-java">package com.springboot.springsecurity.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CreateAccessTokenRequest {
    private String refreshToken;
}
</code></pre>
<pre><code class="language-java">package com.springboot.springsecurity.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class CreateAccessTokenResponse {
    private String accessToken;
}
</code></pre>
<h4 id="6-2-tokenapicontroller-구현">6-2. TokenApiController 구현</h4>
<ul>
<li>Post 요청이 오면 토큰 서비스에서 리프레시 토큰을 기반으로 새로운 액세스 토큰을 만들어준다.<pre><code class="language-java">package com.springboot.springsecurity.controller;
</code></pre>
</li>
</ul>
<p>import com.springboot.springsecurity.dto.CreateAccessTokenRequest;
import com.springboot.springsecurity.dto.CreateAccessTokenResponse;
import com.springboot.springsecurity.service.TokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;</p>
<p>@RequiredArgsConstructor
@RestController
public class TokenApiController {
    private final TokenService tokenService;</p>
<pre><code>@PostMapping(&quot;/api/token&quot;)
public ResponseEntity&lt;CreateAccessTokenResponse&gt; createNewAccessToken(@RequestBody CreateAccessTokenRequest request) throws IllegalAccessException {
    String newAccessToken = tokenService.createNewAccessToken(request.getRefreshToken());
    return ResponseEntity.status(HttpStatus.CREATED).body(new CreateAccessTokenResponse(newAccessToken));
}</code></pre><p>}</p>
<p>```</p>
<hr>
<h2 id="그럼-어디에-jwt는-어디에서-언제-사용이-될까🧐">그럼 어디에 JWT는 어디에서 언제 사용이 될까?🧐</h2>
<blockquote>
</blockquote>
<p><strong>JWT(JSON Web Token)</strong>는 주로 인증 및 정보 교환 목적으로 사용되는 토큰 기반의 인증 메커니즘이기 때문에  <strong><em>클라이언트와 서버 간의 신뢰할 수 있는 정보 교환을 위해 사용</em></strong>된다.</p>
<p><strong>사용자 인증 (Authentication)</strong></p>
<p>사용자 로그인 후 서버는 사용자 정보를 기반으로 JWT를 생성하여 클라이언트에게 전달한다. 이후 클라이언트는 해당 JWT를 요청 헤더에 포함시켜 서버에 요청을 보낸다. 서버는 JWT를 검증하여 사용자를 인증한다.</p>
<p><strong>권한 부여 (Authorization)</strong></p>
<p>JWT는 사용자의 권한 정보를 포함할 수 있기 때문에 따라서 서버는 JWT를 통해 클라이언트의 권한을 확인하고, 특정 자원에 대한 접근을 허용하거나 제한할 수 있다.</p>
<p><strong>정보 교환</strong></p>
<p>JWT는 신뢰할 수 있는 정보 교환을 위해 사용된다. 
그러므로 서버 간에 사용자 정보나 설정 데이터를 주고 받을 때 JWT를 사용하면 데이터의 무결성을 보장할 수 있다.</p>
<hr>
<h2 id="결론">결론</h2>
<blockquote>
</blockquote>
<p><strong>JWT</strong>는 <strong><em>신뢰할 수 있는 정보 교환을 위해 사용</em></strong>된다. 예를 들어, 서버 간에 사용자 정보나 설정 데이터를 주고 받을 때 JWT를 사용하면 <strong><em>데이터의 무결성을 보장</em></strong>할 수 있을 것 같다.</p>
<hr>
<h2 id="참고문헌-및-교재📒">참고문헌 및 교재📒</h2>
<p><a href="https://joie-kim.github.io/JWT-Auth/">https://joie-kim.github.io/JWT-Auth/</a>
스프링 부트 3 백엔드 개발자 되기: 자바 편 - 신선영 -</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱 Spring Security]]></title>
            <link>https://velog.io/@kkong_do/Spring-Security</link>
            <guid>https://velog.io/@kkong_do/Spring-Security</guid>
            <pubDate>Wed, 10 Jul 2024 04:32:37 GMT</pubDate>
            <description><![CDATA[<h2 id="스프링-시큐리티spring-security란">스프링 시큐리티(Spring Security)란?</h2>
<blockquote>
</blockquote>
<p><strong>Spring Security</strong>란 스프링 기반의 애플리케이션 보안(인증, 인가, 권한)을 담당하는 스프링 하위 프레임워크이다.</p>
<ul>
<li>스프링 시큐리티를 이해하기 위해서는 <strong><em>인증(Authentication)</em></strong>과 <strong><em>인가(Authorization)</em></strong>에 대하여 알아야 한다.</li>
</ul>
<hr>
<h3 id="인증authentication과-인가authorization">인증(Authentication)과 인가(Authorization)</h3>
<p>** 인증(Authentication) **</p>
<blockquote>
</blockquote>
<p><strong>인증(Authentication)</strong>은 사용자의 신원을 입증하는 과정</p>
<ul>
<li>사용자가 사이트에 로그인을 할 때 누구인지 확인하는 과정이 인증의 대표적인 예이다.</li>
</ul>
<p>** 인가(Authorization) **</p>
<blockquote>
</blockquote>
<p><strong>인가(Authorization)</strong>는 사이트의 특정 부분에 접근할 수 있는지 권한을 확인하는 작업</p>
<ul>
<li>관리자는 관리자 페이지에 들어갈 수 있지만 일반 사용자는 관리자 페이지에 들어갈 수 없게 권한을 확인하는 과정이 인가의 대표적인 예이다.</li>
</ul>
<p>이러한 스프링 시큐리티는 CSRF 공격(사용자의 권한을 가지고 특정 동작을 수행하도록 유도하는 공격), 세션 고정 공격(사용자의 인증 정보를 탈취하거나 변조하는 공격)을 방어해준다.</p>
<hr>
<h2 id="필터-기반으로-동작하는-스프링-시큐리티🧑🏻⚖️">필터 기반으로 동작하는 스프링 시큐리티🧑🏻‍⚖️</h2>
<ul>
<li><strong>스프링 시큐리티는 필터 기반으로 동작</strong>한다.</li>
<li>다음 참고 사진은 스프링시큐리티의 필터 구조에 대해 나타낸 구조도이다! 유심히 관찰해보자🤓</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/d7e11e92-0cb8-4084-af92-4ee0116c5067/image.png" alt=""></p>
<ul>
<li>스프링 시큐리티는 다양한 필터들로 이루어져 있으며, 각 필터에서 인증과 인가와 관련된 작업을 처리한다.</li>
<li><strong>SecurityContextPersistenceFilter부터 시작해서 아래로 내려가며 FilterSecurityInterceptor까지 순서대로 필터를 거치게 된다.</strong></li>
<li>필터가 실행될 때마다 빨간색 화살표로 연결된 오른쪽 박스의 클래스를 거치며 실행이 된다.</li>
<li>특정 필터를 제거하거나 필터 뒤에 커스텀 필터를 넣는 등의 설정도 가능하다.</li>
</ul>
<blockquote>
</blockquote>
<p>우리는 여기서 <strong><code>UsernamePasswordAuthenticationFilter</code></strong>와 <strong><code>FilterSecurityInterceptor</code></strong>를 집중해서 보아야 한다.</p>
<h3 id="usernamepasswordauthenticationfilter">UsernamePasswordAuthenticationFilter</h3>
<blockquote>
</blockquote>
<p>아이디와 패스워드가 넘어오면 인증 요청을 위임하는 <strong><em>인증 관리자</em></strong> 역할을 수행한다.</p>
<ul>
<li>폼 기반의 로그인을 수행할 때 사용되는 필터로 아이디, 패시워드 데이터를 파싱(Parsing)해 인증 요청을 위임한다.</li>
<li>인증이 성공하면 <code>AuthenticationSuccessHandler</code>를, 인증에 실패하면 <code>AuthenticationFailureHandler</code>를 실행한다.</li>
</ul>
<h3 id="filtersecurityinterceptor">FilterSecurityInterceptor</h3>
<blockquote>
</blockquote>
<p>권한 부여 처리를 위임하여 접근 제어 결정을 쉽게 하는 <strong><em>접근 결정 관리자</em></strong> 역할을 수행한다.</p>
<ul>
<li>AccessDecisionManager로 권한 부여 처리를 위임함으로써 접근 제어 결정을 쉽게 해준다.</li>
<li>이 과정에서는 이미 사용자가 인증되어 있으므로 유효한 사용자인지 아닌지 알 수 있다. 즉, 인가 관련 설정을 할 수 있다.</li>
</ul>
<hr>
<h3 id="폼-로그인-기반-스프링-시큐리티-인증-처리-흐름">폼 로그인 기반 스프링 시큐리티 인증 처리 흐름</h3>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/b9491848-3064-4bae-b037-1771cfe38887/image.png" alt=""></p>
<h4 id="다음-절차를-차근히-따라와-보자🦾">다음 절차를 차근히 따라와 보자🦾</h4>
<ol>
<li>사용자가 폼에 아이디와 패스워드를 입력하면, HTTPServletRequest에 아이디와 비밀번호 정보가 전달된다. 이때 AuthenticationFilter가 넘어온 아이디와 비밀번호의 유효성 검사를 진행한다.</li>
<li>유효성 검사가 끝나면실제 구현체인 UsernamePasswordAuthenticationToken을 만들어 넘겨준다.</li>
<li>전달받은 인증용 객체인 UsernamePasswordAuthenticationToken을 AuthenticationManager에게 보낸다.</li>
<li>UsernamePasswordAuthenticationToken을 AuthenticationProvider에 보낸다.</li>
<li>사용자 아이디를 UserDetailService에 보낸다. UserDetailService는 사용자 아이디로 찾은 사용자의 정보를 UserDetails 객체로 만들어 AuthenticationProvider에게 전달한다.</li>
<li>DB에 있는사용자 정보를 가져온다.</li>
<li>입력 정보와 UserDetails의 정보를 비교해 실제 인증 처리를 한다.</li>
<li>~ 10.까지 인증이 완료되면 SecurityContextHolder에 Authentication을 저장한다. 인증 성공 여부에 따라 성공하면 AuthenticationSucessHandler, 실패하면 AuthenticationFailureHandler를 실행한다.</li>
</ol>
<hr>
<h2 id="spring-security-구현-코드">Spring Security 구현 코드</h2>
<ul>
<li>이번 실습은 Form을 이용해서 로그인/로그아웃을 구현한 코드이다.</li>
</ul>
<h3 id="1-의존성-추가하기buildgradle">1. 의존성 추가하기(build.gradle)</h3>
<pre><code class="language-java">dependencies{
    // 스프링 시큐리티를 사용하기 위한 스타터 추가
    implementation &#39;org.springframework.boot:spring-boot-starter-security&#39;
    // 타임리프에서 스프링 시큐리티를 사용하기 위한 의존성 추가
    implementation &#39;org.thymeleaf.extras:thymeleaf-extras-springsecurity6&#39;
    // 스프링 시큐리티를 테시트하기 위한 의존성 추가
    testImplementation &#39;org.springframework.security:spring-security-test&#39;
}
</code></pre>
<h3 id="2-엔티티-만들기">2. 엔티티 만들기</h3>
<pre><code class="language-java">package me.shinsunyoung.springbootdeveloper.domain;

import jakarta.persistence.*;
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = &quot;users&quot;)
@Builder
public class User implements UserDetails { // UserDetails를 상속받아 인증 객체로 사용
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = &quot;email&quot;, nullable = false, unique = true)
    private String email;
    @Column(name = &quot;password&quot;)
    private String password;

    @Builder
    public User(Long id, String email, String password) {
        this.id = id;
        this.email = email;
        this.password = password;
    }

    @Override // 권한 반환
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return List.of(new SimpleGrantedAuthority(&quot;user&quot;));
    }

    // 사용자의 id를 반환(고유한 값)
    @Override
    public String getUsername() {
        return email;
    }

    // 사용자의 패스워드 반환
    @Override
    public String getPassword(){
        return password;
    }
    // 계정 만료 여부 반환
    @Override
    public boolean isAccountNonExpired() {
        return true; // true면 만료되지 않았음
    }

    // 계쩡 잠금 여부 반환
    @Override
    public boolean isAccountNonLocked() {
        return true; // true면 계정 잠금 X
    }

    // 패스워드의 만료 여부 반환
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    // 계정 사용 가능 여부 반환
    @Override
    public boolean isEnabled() {
        return true;
    }
}</code></pre>
<ul>
<li>유의해야할 점은 <strong>UserDetails를 꼭 implements 받아서 구현</strong>하여야 한다.</li>
</ul>
<h4 id="userdetail-인터페이스에-선언된-메서드📒">UserDetail 인터페이스에 선언된 메서드📒</h4>
<table>
<thead>
<tr>
<th>메서드</th>
<th>반환 타입</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>getAuthorities</td>
<td>Collection&lt; ? extends GrantedAuthority&gt;</td>
<td>사용자가 가지고 있는 권한의 목록을 반환한다.</td>
</tr>
<tr>
<td>getUsername()</td>
<td>String</td>
<td>사용자를 식별할 수 있는 사용자 이름을 반환한다. 이때 사용되는 사용자의 이름은 반드시 고유해야 한다.  현재 코드는 email이 고유 코드이다.</td>
</tr>
<tr>
<td>getPassword()</td>
<td>String</td>
<td>사용자의 비밀번호를 반환한다. 이때 저장되어 있는 비밀번호는 암호화해서 저장해야 한다.</td>
</tr>
<tr>
<td>isAccountNonExpired()</td>
<td>boolean</td>
<td>계정이 만료되었는지 확인하는 메서드이다. 만약 만료되지 않았따면 true를 반환한다.</td>
</tr>
<tr>
<td>isAccountNonLocked()</td>
<td>boolean</td>
<td>계정이 만료되었는지 확인하는 메서드이다. 만약 만료되지 않았따면 true를 반환한다.</td>
</tr>
<tr>
<td>isCredetialsNonExpired()</td>
<td>boolean</td>
<td>비밀번호가 만료되었는지 확인하는 메서드이다. 만약 만료되지 않았을 때는 true를 반환한다.</td>
</tr>
<tr>
<td>isEnabled()</td>
<td>boolean</td>
<td>계정이 사용 가능한지 확인하는 메서드이다. 만약 사요 가능하다면 true 반환하다.</td>
</tr>
</tbody></table>
<h3 id="3-리포지터리-만들기">3. 리포지터리 만들기</h3>
<ul>
<li>User 엔티티에 대한 리포지토리 만들기<pre><code class="language-java">package me.shinsunyoung.springbootdeveloper.repository;
</code></pre>
</li>
</ul>
<p>import me.shinsunyoung.springbootdeveloper.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;</p>
<p>import java.util.Optional;</p>
<p>@Repository
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    Optional<User> findByEmail(String email);
}</p>
<pre><code>### 4. 서비스 메서드 코드 작성하기
- 엔티티와 리포지터리가 완성되었으니 스프링 시큐리티에서 로그인을 진행할 때 사용자 정보를 가져오는 코드 작성
```java
package me.shinsunyoung.springbootdeveloper.service;

import lombok.RequiredArgsConstructor;
import me.shinsunyoung.springbootdeveloper.repository.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
// 스프링 시큐리티에서 사용자 정보를 가져오는 인터페이스
public class UserDetailService implements UserDetailsService {
    private final UserRepository userRepository;

    // 사용자 이름(email)으로 사요앚의 정보를 가져오는 메서드
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        return userRepository.findByEmail(email).orElseThrow(() -&gt; new IllegalArgumentException(email));
    }
}
</code></pre><ul>
<li>스프링 시큐리티에서 사용자의 정보를 가져오는 UserDetailsService 인터페이스를 구현한다.</li>
<li><code>loadUserByUsername()</code> 메서드를 오버라이딩해서 사용자 정보를 가져오는 로직 작성</li>
</ul>
<h3 id="5-시큐리티-설정하기">5. 시큐리티 설정하기</h3>
<ul>
<li>실제 인증 처리를 하는 시큐리티 설정 파일 WebSecurityConfig파일을 작성한다.</li>
</ul>
<pre><code class="language-java">package me.shinsunyoung.springbootdeveloper.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toH2Console;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
    private final UserDetailsService userService;

    @Bean // 1. 스프링 시큐리티 기능 비활성화
    public WebSecurityCustomizer configure() {
        return (web) -&gt; web.ignoring()
                .requestMatchers(new AntPathRequestMatcher(&quot;/static/**&quot;));
    }

    @Bean // 2. 특정 HTTP 요청에 대한 웹 기반 보안 구성
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(auth -&gt; auth // 3. 인증, 인가 설정
                        .requestMatchers(
                                new AntPathRequestMatcher(&quot;/login&quot;),
                                new AntPathRequestMatcher(&quot;/signup&quot;),
                                new AntPathRequestMatcher(&quot;/user&quot;)
                        ).permitAll()
                        .anyRequest().authenticated())
                .formLogin(formLogin -&gt; formLogin // 4. 폼 기반 로그인 설정
                        .loginPage(&quot;/login&quot;)
                        .defaultSuccessUrl(&quot;/articles&quot;)
                )
                .logout(logout -&gt; logout.logoutSuccessUrl(&quot;/login&quot;) // 5. 로그아웃 설정
                        .invalidateHttpSession(true)
                )
                .csrf(AbstractHttpConfigurer::disable) // 6. CSRF 비활성화
                .build();
    }
    // 7. 인증 관리자 관련 설정
    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http, BCryptPasswordEncoder bCryptPasswordEncoder, UserDetailsService userDetailsService) throws Exception {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userService);
    // 8. 사용자 정보 서비스를 설정
        authProvider.setPasswordEncoder(bCryptPasswordEncoder);
        return new ProviderManager(authProvider);
    }
    // 9. 패스워드 인코더로 사용할 빈 등록
    @Bean 
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
</code></pre>
<h4 id="번호에-대한-설명">번호에 대한 설명</h4>
<ul>
<li>1번 : 스프링 시큐리티의 모든 기능을 사용하지 않게 하는 코드이다. 해당 코드를 작성한 이유는 인증과 인가 서비스를 모든 곳에 적용하지 않게 하기 위해서다. 일반적으로 static resources에 설정한다.</li>
<li>2번 : 특정 HTTP 요청에 대해 웹 기반 보안을 구성하는 코드이다. 이 메서드에서 인증/인가 및 로그인, 로그아웃 관련 설정을 할 수 있다.</li>
<li>3번 : 특정 경로에 대한 액세스를 설정하는 코드이다. <ul>
<li><code>requestMatchers()</code> : 특정 요청과 일치하는 url에 대한 액세스를 설정한다.</li>
<li><code>permitAll()</code> : 누구나 접근이 가능하게 설정한다. 즉, &quot;/login&quot;, &quot;/signup&quot;, &quot;/user&quot; 로 요청이 오면 인증/인가 없이도 접근이 가능하다.</li>
<li><code>anyRequest()</code> : 위에서 설정한 url이외의 요청에 대해 설정한다.</li>
<li><code>authenticated()</code> : 별도의 인가는 필요하지 않지만 인증이 성공된 상태여야 접근이 가능하다.</li>
</ul>
</li>
<li>4번 : 폼 기반 로그인 설정을 진행한다.<ul>
<li><code>loginPage()</code> : 로그인 페이지 경로를 설정한다.<ul>
<li><code>defaultSucessUrl()</code> : 로그인이 완료되었을 때 이동할 경로를 설정한다.</li>
</ul>
</li>
</ul>
</li>
<li>5번 : 로그아웃 설정을 진행한다.<ul>
<li><code>logoutSuccessUrl()</code> : 로그아웃이 완료되었을 때 이동할 경로를 설정한다.</li>
<li><code>invalidateHttpSession()</code> : 로그아웃 이후에 세션을 전체 삭제할지 여부를 설정한다.</li>
</ul>
</li>
<li>6번 : CSRF 설정을 비활성화한다. CSRF 공격을 방지하기 위해서는 활성화를 해야한다.</li>
<li>7번 : 인증 관리자 관련 설정을 진행한다. 사용자 정보를 가져올 서비시를 재정의하거나, 인증 방법, 예를 들어 JDBC 기반 인 등을 설정한다.</li>
<li>8번 : 사용자 서비스를 설정한다.<ul>
<li><code>userDetailsService()</code> : 사용자 정보를 가져올 서비스를 설정한다. 이때 설정하는 서비스 클래스는 UserDetailsService를 상속받은 클래스여야 한다.</li>
<li><code>passwrodEncoder()</code> : 비밀번호를 암호화하기 위한 인코더를 설정한다.</li>
</ul>
</li>
<li>9번 : 패스워드 인코더를 빈으로 등록한다.</li>
</ul>
<h3 id="6-회원-가입-구현하기">6. 회원 가입 구현하기</h3>
<h4 id="6-1-서비스-메서드-코드-작성하기">6-1. 서비스 메서드 코드 작성하기</h4>
<pre><code class="language-java">package me.shinsunyoung.springbootdeveloper.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class AddUserRequest {
    private String email;
    private String password;
}</code></pre>
<ul>
<li>사용자 정보를 담고 있는 객체를 작성한다.<pre><code class="language-java">package me.shinsunyoung.springbootdeveloper.service;
</code></pre>
</li>
</ul>
<p>import lombok.RequiredArgsConstructor;
import me.shinsunyoung.springbootdeveloper.domain.User;
import me.shinsunyoung.springbootdeveloper.dto.AddUserRequest;
import me.shinsunyoung.springbootdeveloper.repository.UserRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;</p>
<p>@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;</p>
<pre><code>public Long save(AddUserRequest dto){
    return userRepository.save(User.builder()
            .email(dto.getEmail())
            // 패스워드 암호화
            .password(bCryptPasswordEncoder.encode(dto.getPassword()))
            .build()).getId();
}</code></pre><p>}</p>
<pre><code>- AddUserRequest 객체를 인수로 받는 회원 정보 추가 메서드를 작성한다. UserService를 만든다.
#### 6-2 컨트롤러 코드 작성하기
```java
package me.shinsunyoung.springbootdeveloper.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import me.shinsunyoung.springbootdeveloper.dto.AddUserRequest;
import me.shinsunyoung.springbootdeveloper.service.UserService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@RequiredArgsConstructor
@Controller
public class UserApiController {
    private final UserService userService;

    @PostMapping(&quot;/user&quot;)
    public String signup(AddUserRequest request) {
        userService.save(request); // 회원 가입 메서드 호출
        return &quot;redirect:/login&quot;; // 회원 가입이 완료된 이후에 로그인 페이지로 이동한다.
    }

    @GetMapping(&quot;/logout&quot;)
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        new SecurityContextLogoutHandler().logout(request, response, SecurityContextHolder.getContext().getAuthentication());
        return &quot;redirect:/login&quot;;
    }
}
</code></pre><ul>
<li>회원 가입 폼에서 회원 가입요청을 받으면 서비스 메서드를 사용하여 사용자를 저장한뒤 로그인 페이지로 redirect하는 메서드를 작성한다.</li>
</ul>
<h2 id="실제-구현한-코드의-실행-결과🔫">실제 구현한 코드의 실행 결과🔫</h2>
<p><code>http://localhost:8080/articles</code>로 접근하게 되면 articles는 인증된 사용자만 들어갈 수 있는 페이지이므로 로그인 페이지로 redirect 된다.
<img src="https://velog.velcdn.com/images/kkong_do/post/701c57f9-e0dc-46fb-adde-7f2f23711ea0/image.png" alt=""></p>
<p>회원가입 버튼을 누르게 되면 회원 가입 페이지로 이동하고 회원 가입 페이지는 <code>permitAll()</code> 메서드이기 때문에 별도 인증 없이 접근이 가능하다.
<img src="https://velog.velcdn.com/images/kkong_do/post/84ceab47-14a6-42a9-9da2-294174605228/image.png" alt=""></p>
<p>비밀번호가 암호화되어 저장되어있는 것을 확인할 수 있다..
<img src="https://velog.velcdn.com/images/kkong_do/post/a6e04813-79bc-476c-843a-22149280e0cb/image.png" alt=""></p>
<p>회원가입을 진행하게 되면 로그인 페이지로 이동하게 되고 가입한 이메일과 비밀번호를 입력하여 로그인을 진행한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/a8acae50-cc02-458e-ac6c-4a6e9a6baf3b/image.png" alt=""></p>
<p>정상적으로 로그인이 성공하면 글 목록 페이지로 이동하게 된다.
<img src="https://velog.velcdn.com/images/kkong_do/post/201768c4-2843-4f5a-bc1d-dada88c3bd48/image.png" alt=""></p>
<p>로그아웃 버튼을 누르게 디면 인증 정보가 없으므로 다시 로그인 페이지로 이동한다.</p>
<hr>
<h2 id="그럼-어디에-spring-security가-사용이-될까🧐">그럼 어디에 Spring Security가 사용이 될까?🧐</h2>
<blockquote>
</blockquote>
<p>전체 플랫폼의 로그인/로그아웃과 특정 페이지에 대한 접근을 특정 사용자에게만 해줄 때 유용하게 사용될 것 같다.</p>
<hr>
<h2 id="정리">정리</h2>
<blockquote>
</blockquote>
<p><strong>스프링 시큐리티</strong>는 스프링 기반의 애플리케이션 보안(인증, 인가, 권한)을 담당하는 스프링 하위 프레임워크이며 스프링 시큐리티는 필터 기반으로 동작한다. 
각 필터에서는 인증과 인가와 관련된 작업을 처리하고 기본적으로 세션과 쿠기 방식으로 인증을 처리한다. </p>
<ul>
<li>스프링 시큐리티에서는 사용자의 인증과인가 정보를 <code>UserDetails</code> 객체에 담는다. 이 클래스를 상속받은 뒤 메서드를 오버라이드해 사용하면 된다.</li>
<li>스프링 시큐리티에서 사용자의 정보를 가져오는 데 사용하는 <code>UserDetailService</code>를 사용한다. 이 클래스를 상속받은 뒤 <code>loadUserByUsename()</code>을 오버라이드하면 스프링 시큐리티에서 사용자의 정보를 가져올 때 오버라이드된 메서드를 사용한다.</li>
</ul>
<hr>
<h2 id="참고-문헌-및-교재📒">참고 문헌 및 교재📒</h2>
<p><a href="http://atin.tistory.com/590?pidx=0">http://atin.tistory.com/590?pidx=0</a>
<a href="https://taetoungs-branch.tistory.com/204?pidx=1">https://taetoungs-branch.tistory.com/204?pidx=1</a>
스프링 부트 3 백엔드 개발자 되기: 자바 편 - 신선영 -</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱Spring Batch (2) - Tasklet 방식, Chunk 방식, 그리고 간단한 구현]]></title>
            <link>https://velog.io/@kkong_do/Spring-Batch-2-Tasklet-%EB%B0%A9%EC%8B%9D-Chunk-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@kkong_do/Spring-Batch-2-Tasklet-%EB%B0%A9%EC%8B%9D-Chunk-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Tue, 09 Jul 2024 06:19:39 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-boot-batch-종류가-무엇이-있을까🧐">Spring Boot Batch 종류가 무엇이 있을까?🧐</h2>
<h3 id="step이-구성하는-대표적인-두가지-방식이-존재한다">Step이 구성하는 대표적인 두가지 방식이 존재한다.</h3>
<ul>
<li>Tasklet 방식</li>
<li>Chunk 방식</li>
</ul>
<hr>
<h2 id="tasklet-방식">Tasklet 방식</h2>
<blockquote>
</blockquote>
<p><strong>Tasklet 방식</strong>은 Tasklet을 이용한 Task 기반의 처리 방식이며, 
<strong><em>Batch의 Step 단계에서 단일한 레코드(row)나 파일을 하나의 작업만 처리하는 방식</em></strong>을 의미한다. Tasklet 방식은 각각의 처리를 하나의 트랜잭션으로 처리한다.</p>
<ul>
<li>일반적인 수행 절차가 파일을 읽고 처리한 다음 결과를 데이터베이스에 쓰는 작업을 수행하며 이러한 단일 작업을 처리하기 때문에 작업이 끝날 때까지 대기해야 한다. 즉, 대용량 데이터 처리에는 적합하지 않다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/5e091b1d-65e2-4cfa-bbd7-9d14b03d4071/image.png" alt=""></p>
<h2 id="chunk-방식">Chunk 방식</h2>
<blockquote>
</blockquote>
<p><strong>Chunk 방식</strong>은 Chuck을 이용한 Chunk 기반 처리 방식이며, <strong><em>Batch의 Step 단계에서 단일 한 레코드를 묶어서 여러 작업을 처리하는 방식</em></strong>을 의미한다.
Chunk방식은 묶인 레코드를 하나의 트랜잭션으로 처리하며 실패를 하는 경우 롤백을 수행한다.</p>
<ul>
<li>Chunk 방식은 병렬 처리를 위해 Chunk를 사용하되 순차적으로 처리하는 방식이다.</li>
<li>Parallel Chunk 방식은 Chunk를 독립적으로 처리하여 병렬적인 처리를 수행한다.</li>
<li>대용량 데이터를 처리할 때 성능이 향상되고, 중복 레코드 처리나 실패한 레코드 처리 등 예외 상황에 대한 대처가 용이하다. &lt;=&gt;Chunk 방식과의 차이점</li>
<li>Ex. 파일을 읽어 들여 데이터를 처리하는 작업이나 DB에서 데이터를 조회하여 처리하는 작업 등이 존재한다.</li>
</ul>
<h3 id="chunk란">Chunk란?</h3>
<blockquote>
</blockquote>
<p>Chunk란 데이터를 일정한 크기로 나눈 데이터 셋(Data Set)을 의미한다. 즉, <strong><em>각 커밋 사이에 처리될 row(item) 수</em></strong>를 의미한다.</p>
<ul>
<li>Chunk 단위로 나누게 되면 전체 데이터를 한번에 처리하지 않기 때문에 메모리 부하를 줄이고 성능을 향상시킬 수 있다.</li>
<li>성공 시 Chunk만큼 커밋, 실패 시 Chunk만큼 롤백을 수행한다.</li>
</ul>
<h3 id="단일한-레코드를-묶어서-처리한다의-의미">&#39;단일한 레코드를 묶어서 처리한다&#39;의 의미</h3>
<blockquote>
</blockquote>
<ul>
<li><p>Chunk 방식에서는 ‘Reader-Processor-Writer 방식’을 이용한다.</p>
</li>
<li><p>이러한 처리 방식은 대용량 처리를 위해서 사용한다.</p>
</li>
<li><p><strong>‘Reader’</strong>는 데이터를 읽어 들이는 역할을 한다.</p>
</li>
<li><p><strong>‘Processor’</strong>는 읽어 들인 데이터를 가공하거나 필터링하는 역할을 한다. </p>
</li>
<li><p><strong>‘Writer’</strong>는 가공된 데이터를 저장하는 역할을 수행한다.</p>
</li>
</ul>
<h3 id="chunk-기반의-step의-reader-processor-writer-처리-방식의-흐름">Chunk 기반의 Step의 Reader-Processor-Writer 처리 방식의 흐름</h3>
<ol>
<li>데이터 베이스에서 데이터를 읽어온다. (Item Reader)</li>
<li>읽어온 데이터를 처리한다. (Item Processor)</li>
<li>데이터를 저장한다. (Item Writer)</li>
</ol>
<h4 id="itemreader">ItemReader</h4>
<blockquote>
</blockquote>
<p>Cursor와 Paging 기능을 제공한다.</p>
<h4 id="itemprocessor">ItemProcessor</h4>
<blockquote>
</blockquote>
<ul>
<li>Chunk-oriented Tasklet을 구성할 떄 선택 요소이다. </li>
<li>데이터를 가공/필터링 하는 역할을 수행(writer에서도 구현 가능한 역할을 하며 비즈니스 코드가 섞이는 것을 방지한다.)하며 Step에 여러 로직이 필요할 때 도입을 고려하여 유지보수성을 증가한다.</li>
<li>데이터 처리를 실패했을 때 null을 반환하여 writer에 전달되지 않게 한다.</li>
</ul>
<h4 id="itemwriter">ItemWriter</h4>
<blockquote>
</blockquote>
<p>Chunk 단위 만큼 한번에 작업을 수행한다. </p>
<h4 id="chunk-기반-처리-방식">Chunk 기반 처리 방식</h4>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/f85e4527-f35a-4dab-9414-ca8d352bc927/image.png" alt=""></p>
<hr>
<h2 id="tasklet과-chunk를-비교하기🤺">Tasklet과 Chunk를 비교하기🤺</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>Tasklet</th>
<th>Chunk</th>
</tr>
</thead>
<tbody><tr>
<td>실행 시점</td>
<td>STEP 실행 중</td>
<td>STEP 실행 전</td>
</tr>
<tr>
<td>실행 방식</td>
<td>‘작은 단위’로 분할하여 실행</td>
<td>‘하나의 큰 덩어리’로 실행</td>
</tr>
<tr>
<td>커밋 방식</td>
<td>‘Tasklet’ 단위로 처리 후 커밋</td>
<td>‘Chunk’ 단위로 처리 후 커밋</td>
</tr>
<tr>
<td>배치 성능</td>
<td>‘작은 단위’의 데이터 처리 시 유리</td>
<td>&#39;대량의 데이터’ 처리 시 유리</td>
</tr>
<tr>
<td>재시작</td>
<td>실패한 태스크릿만 다시 실행</td>
<td>실패 시 청크 전체 다시 실행</td>
</tr>
<tr>
<td>가독성</td>
<td>코드가 분할되어 가독성 향상</td>
<td>코드가 길어져 가독성 저하</td>
</tr>
<tr>
<td>유지보수</td>
<td>수정이 용이</td>
<td>수정이 어려움</td>
</tr>
</tbody></table>
<hr>
<h2 id="parallel-chunk-방식">Parallel Chunk 방식</h2>
<blockquote>
</blockquote>
<p><strong>Parallel Chunk 방식</strong>이란 Chunk 방식의 처리에서 더욱 빠른 처리 속도를 위해 Chunk를 독립적으로 처리하여 여러 개의 ‘Chunk를 병렬로 처리’한다. 병렬 처리를 통해 처리 속도를 높일 수 있다는 장점이 있다.</p>
<ul>
<li>예를 들어, 여러 대의 서버에서 동시에 작업을 처리해야 할 때 사용됩니다.</li>
</ul>
<h3 id="parallel-chunk-수행-과정">Parallel Chunk 수행 과정</h3>
<ol>
<li>데이터를 적절한 크기로 분할한다.</li>
<li>분할된 각각의 부분을 병렬처리할 수 있도록 작업을 분배한다.</li>
<li>각각의 부분을 병렬처리한다.</li>
<li>처리된 결과를 다시 합친다.</li>
</ol>
<h2 id="remote-chunking-방식">Remote Chunking 방식</h2>
<blockquote>
</blockquote>
<p><strong>Remote Chunking 방식이란</strong> <strong><em>여러 대의 서버에서 대용량 데이터 처리를 수행할 때 사용</em></strong>되며 서버 간에 데이터를 공유하고, 각각의 서버에서 병렬로 처리한다.</p>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/c72260fb-a413-4d5e-b770-8f9f97d4511b/image.png" alt=""></p>
<hr>
<h2 id="구현-방법tasklet-기반-배치">구현 방법(tasklet 기반 배치)</h2>
<h3 id="1-buildgradle에-의존성-추가">1. build.gradle에 의존성 추가</h3>
<pre><code class="language-java">//build.gradle
implementation &#39;org.springframework.boot:spring-boot-starter-batch&#39;
testImplementation &#39;org.springframework.batch:spring-batch-test&#39;implementation &#39;org.springframework.boot:spring-boot-starter-batch&#39;</code></pre>
<h3 id="2-batchconfig-클래스에서-원하는-job과-step을-작성한다">2. BatchConfig 클래스에서 원하는 Job과 Step을 작성한다.</h3>
<pre><code class="language-java">package com.springboot.springbatchtutorial.config;

import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@RequiredArgsConstructor  // Lombok 애노테이션으로 final 또는 @NonNull 필드에 대한 생성자를 자동으로 생성한다.
public class BatchConfig extends DefaultBatchConfiguration {
    @Bean
    public Job testJob(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws DuplicateJobException {
        // Job을 정의하고 시작할 Step을 지정한다.
        // JobBuilder를 사용하여 &quot;testJob&quot;이라는 이름의 잡을 만든다.
        Job job = new JobBuilder(&quot;testJob&quot;, jobRepository)
                .start(testStep(jobRepository, transactionManager))
                .build();
        return job;
    }

    public Step testStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        // Step을 정의하고 Tasklet을 지정한다.
        // StepBuilder를 사용하여 &quot;testStep&quot;이라는 이름의 스텝을 만든다.
        Step step = new StepBuilder(&quot;testStep&quot;, jobRepository)
                .tasklet(testTasklet(), transactionManager) // testTasklet을 tasklet으로 설정하고 트랜잭션 매니저를 사용한다.
                .build();
        return step;
    }

    public Tasklet testTasklet() {
        // Tasklet을 정의하고 비즈니스 로직을 작성
        // Tasklet을 생성합니다.
        return ((contribution, chunkContext) -&gt; {
            System.out.println(&quot;***** 10초마다 &#39;Hello batch&#39; 출력!! *****&quot;); // 콘솔에 출력합니다.
            // 원하는 비지니스 로직 작성
            return RepeatStatus.FINISHED; // 작업이 완료되었음을 나타낸다.
        });
    }
}</code></pre>
<h3 id="3-batchscheduler-클래스에서-schedueld-어노테이션을-사용하여-job의-실행-조건-설정">3. BatchScheduler 클래스에서 @Schedueld 어노테이션을 사용하여 Job의 실행 조건 설정</h3>
<pre><code class="language-java">package com.springboot.springbatchtutorial.scheduler;

import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@RequiredArgsConstructor // Lombok 애노테이션으로 final 또는 @NonNull 필드에 대한 생성자를 자동으로 생성한다.
@Component // Spring Bean으로 등록됨을 나타낸다.
public class BatchScheduler {

    private final JobLauncher jobLauncher;
    private final JobRegistry jobRegistry;

    @Bean
    public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(){
        // JobRegistryBeanPostProcessor 빈을 생성한다.
        JobRegistryBeanPostProcessor jobProcessor = new JobRegistryBeanPostProcessor();
        jobProcessor.setJobRegistry(jobRegistry);
        return jobProcessor;
    }

    @Scheduled(cron = &quot;0/10 * * * * *&quot;) // 10초마다 실행, // 10초마다 스케줄링하여 실행된다.
    public void runJob() {
        String time = LocalDateTime.now().toString(); // 현재 시간을 문자열로 변환한다.
        try {
            Job job = jobRegistry.getJob(&quot;testJob&quot;); // jobRegistry에서 &quot;testJob&quot;을 가져온다.
            JobParametersBuilder jobParam = new JobParametersBuilder().addString(&quot;time&quot;, time); // JobParametersBuilder에 현재 시간을 추가한다.
            jobLauncher.run(job, jobParam.toJobParameters()); // Job을 실행한다.
        } catch (NoSuchJobException e) {
            throw new RuntimeException(e); // Job을 찾을 수 없는 예외를 처리한다.
        } catch (JobInstanceAlreadyCompleteException |
                 JobExecutionAlreadyRunningException |
                 JobParametersInvalidException |
                 JobRestartException e
        ) {
            throw new RuntimeException(e); // 여러 예외를 처리한다.
        }
    }
}</code></pre>
<h3 id="4-application-클래스-내-enablescheduling-어노테이션-추가하기">4. Application 클래스 내 @EnableScheduling 어노테이션 추가하기</h3>
<pre><code class="language-java">package com.springboot.springbatchtutorial;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringBatchTutorialApplication {

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

}</code></pre>
<h3 id="applicationproperties에-설정-코드-추가">application.properties에 설정 코드 추가</h3>
<pre><code class="language-java"># Spring Batch 작업 자동 실행 비활성화
spring.batch.job.enabled=false
# Spring Batch가 시작될 때 스키마를 항상 초기화 (필요에 따라 데이터베이스 테이블을 생성)
spring.batch.jdbc.initialize-schema=always
# 빈 정의 덮어쓰기를 허용 (동일한 이름의 빈을 다른 설정으로 정의할 수 있음)
spring.main.allow-bean-definition-overriding=true</code></pre>
<h3 id="전체적인-실행-흐름🏄🏻♂️">전체적인 실행 흐름🏄🏻‍♂️</h3>
<ol>
<li>SpringBatchTutorialApplication 클래스가 애플리케이션을 시작한다.</li>
<li>BatchConfig 클래스에서 testJob과 testStep이 정의된다.</li>
<li>BatchScheduler 클래스에서 매 10초마다 testJob이 실행되도록 스케줄링된다.</li>
<li>testJob이 실행될 때마다 testTasklet이 호출되어 <strong>*&quot;10초마다 &#39;Hello batch&#39; 출력!!&quot;*</strong> 메시지를 출력한다.</li>
</ol>
<h3 id="전체-파일디렉토리-구조">전체 파일디렉토리 구조</h3>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/27aed1ca-17d7-4ecd-a782-2cc250184d0f/image.png" alt=""></p>
<h3 id="실제-구현한-코드의-실행-결과🔫">실제 구현한 코드의 실행 결과🔫</h3>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/64173fff-53ba-4230-b475-27ec9affde8b/image.png" alt=""></p>
<hr>
<h2 id="그럼-어디에-spring-batch가-사용이-될까🧐">그럼 어디에 Spring Batch가 사용이 될까?🧐</h2>
<ul>
<li><p>데이터베이스 간의 대량 데이터 전송이나 데이터베이스에서 파일로, 또는 파일에서 데이터베이스로 데이터를 마이그레이션하는 작업에 사용된다.
<code>예시: A라는 데이터베이스에서 B라는 데이터베이스로 모든 고객 데이터를 이전하는 작업.</code></p>
</li>
<li><p>다양한 소스에서 데이터를 추출(Extract)하고, 필요한 형식으로 변환(Transform)한 후, 최종적으로 타겟 데이터베이스나 파일에 적재(Load)하는 작업을 수행한다.
<code>예시: 여러 CSV 파일에서 데이터를 추출하고, 특정 비즈니스 로직에 따라 데이터를 변환한 후, 데이터베이스에 적재하는 작업.</code></p>
</li>
<li><p>정기적으로 대량의 데이터를 처리하여 보고서를 생성하는 작업에 사용된다.
<code>예시: 매월 마지막 날에 지난 달의 매출 데이터를 집계하여 PDF 형식의 보고서를 생성하는 작업.</code></p>
</li>
<li><p>로그 데이터, 트랜잭션 데이터 등의 대량 데이터를 정리하고 집계하는 작업에 사용된다.
<code>예시: 하루 동안의 웹 서버 로그를 수집하여 방문자 수, 페이지 뷰 수 등을 집계하는 작업.</code></p>
</li>
<li><p>정기적으로 데이터베이스를 백업하거나 복원하는 작업을 자동화할 수 있다.
<code>예시: 매일 밤 데이터베이스를 백업하고, 백업 파일을 특정 위치에 저장하는 작업.</code></p>
</li>
<li><p>대용량 파일(예: CSV, XML, JSON 등)을 읽고, 처리한 후 결과를 저장하는 작업에 사용된다.
<code>예시: 수백만 건의 레코드가 포함된 CSV 파일을 읽어 각 레코드를 데이터베이스에 저장하는 작업.</code></p>
</li>
<li><p>정기적으로 데이터베이스의 데이터 정합성을 검증하고, 이상이 있는 데이터를 별도로 처리하는 작업을 수행할 수 있다.
<code>예시: 매주 데이터베이스의 고객 데이터 정합성을 검증하고, 누락된 데이터를 보고서로 생성하여 관리자에게 알리는 작업.</code></p>
</li>
<li><p>특정 이벤트나 조건에 따라 대량의 이메일을 발송하는 작업을 자동화할 수 있다.
<code>예시: 특정 프로모션 기간에 이벤트에 참여한 사용자들에게 이메일을 발송하는 작업.</code></p>
</li>
<li><p>대량의 데이터를 분석하고, 머신러닝 모델을 적용하여 예측 결과를 저장하는 작업을 할 수 있다.
<code>예시: 고객 데이터를 분석하여 고객 이탈을 예측하고, 결과를 데이터베이스에 저장하는 작업.</code></p>
</li>
</ul>
<blockquote>
</blockquote>
<p>이와 같이 <strong>Spring Boot Batch</strong>를 다양한 시나리오에서 사용한다면 <strong><em>반복적으로 수행해야하는 작업이나 대량 데이터 처리 작업을 안정적이고 효율적으로 자동화</em></strong>하기 편할 것이다.</p>
<hr>
<h2 id="spring-boot-2-vs-spring-boot-3-주의해야-할-점">Spring Boot 2 vs Spring Boot 3 주의해야 할 점</h2>
<ul>
<li><p>이전 버전까지 @EnableBatchProcessing 를 통해 배치 실행에 필요한 Bean들이 AutoConfiguration 에 의해 등록이 되었다면, 이제는 아무런 영향을 미치지 않는다.
오히려 있으면 이전과 같이 배치 플로우가 동작을 하지 않는다. SpringBoot 를 이용하고 있고, 이전에 어노테이션으로 BatchRunner 를 생성했었다면, @EnableBatchProcessing 을 빼주어야 한다.</p>
</li>
<li><p>Ordering 을 통해, Configuration 을 각각 구성하여 하나의 애플리케이션에서 Multiple Job 을 구성한 케이스도 있었는데, 이제는 지원하지 않는다. (1 Application 1 Job 전략)</p>
</li>
</ul>
<ul>
<li><p>JobBuilderFactory, StepBuilderFactory 는 이제 Deprecated 되었다.
(TO BE) 이제는 다음과 같이 JobBuilder, StepBuilder 를 이용하여 Job, Step 을 구성해야한다.</p>
</li>
<li><p>Step의 chunk, tasklet 에서 transactionManager 를 인자로 넣어줘야 한다.</p>
</li>
</ul>
<p>여기 기재한 것 이외에도 Spring Boot 3로 넘어오면서 Spring Boot Batch에 많은 변화들이 생겼다. 만약 코드를 작성하고 구동할 때 에러가 발생한다면 찾아보는걸 추천한다.😫</p>
<hr>
<h3 id="참고-문헌">참고 문헌</h3>
<ul>
<li><a href="https://spring.io/batch">https://spring.io/batch</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#domainLanguageOfBatch">https://docs.spring.io/spring-batch/reference/#domainLanguageOfBatch</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#job">https://docs.spring.io/spring-batch/reference/#job</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#configureJob">https://docs.spring.io/spring-batch/reference/#configureJob</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#remote-chunking">https://docs.spring.io/spring-batch/reference/#remote-chunking</a></li>
<li><a href="https://adjh54.tistory.com/169">https://adjh54.tistory.com/169</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱Spring Batch (1) - 배치와 Job, Step은 무엇일까?🧐]]></title>
            <link>https://velog.io/@kkong_do/Spring-Batch-eejvsq3w</link>
            <guid>https://velog.io/@kkong_do/Spring-Batch-eejvsq3w</guid>
            <pubDate>Tue, 09 Jul 2024 02:12:24 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-batch란">Spring Batch란?</h2>
<blockquote>
</blockquote>
<p><strong>Spring Batch</strong>는 로깅/추적, 트랜잭션 관리, 작업 처리 통계, 작업 다시 시작, 건너뛰기 및 리소스 관리를 포함하여 대량의 레코드를 처리하는 데 필수적인 재사용 가능한 기능을 제공하는 프레임워크를 의미한다.</p>
<ul>
<li>대량의 데이터를 처리하는 작업을 의미하며, 이러한 처리 작업을 &#39;자동화&#39;하여 시스템의 부하를 줄이고 효율적으로 데이터를 처리한다.</li>
</ul>
<h3 id="배치-프로그램batch-program">배치 프로그램(Batch Program)</h3>
<ul>
<li>대량의 데이터를 처리하는 작업을 자동화하는 프로그램을 의미한다.</li>
<li>보통 스케쥴러를 이용하여 특정 시간에 수행하도록 구성한다.</li>
<li>예를 들어 매일 새벽 3시에 일괄적으로 데이터를 업데이트하는 작업이 배치 프로그램의 대표적인 예시이다.</li>
</ul>
<h3 id="스케줄러scheduler">스케줄러(Scheduler)</h3>
<ul>
<li><strong>일정한 시간 간격으로 반복적으로 수행</strong>되거나 <strong>특정 시간에 수행되도록 예약해 놓은 작업을 자동으로 실행</strong>해주는 시스템을 의미한다.</li>
</ul>
<h4 id="배치-프로그램과-스케줄러의-차이점🧐">배치 프로그램과 스케줄러의 차이점🧐</h4>
<ul>
<li>비슷한 기능이지만 실행 방법과 목적에 따라 엄연히 다르다.</li>
<li><strong>배치 프로그램</strong>은 <u>일괄 처리를 위한 프로그램이며 정해진 시간에 실행되지 않고 사용자의 명령이 있을 때 실행</u>하는 반면에</li>
<li><strong>스케줄러</strong>는 <u>정해진 시간에 실행되는 프로그램이며, 주기적으로 실행되는 작업</u>을 설정할 수 있는 시스템을 의미한다.</li>
</ul>
<hr>
<h3 id="배치-사용-목적">배치 사용 목적</h3>
<table>
<thead>
<tr>
<th>목적</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>대량의 데이터 처리</td>
<td>대규모 데이터베이스, 로그 파일, CSV 파일 등에서 &#39;데이터 추출 및 처리&#39;를 위해 사용이 된다.</td>
</tr>
<tr>
<td>자동화된 작업 처리</td>
<td>스케줄러를 이용하여 주기적으로 &#39;반복되는 작업&#39;을 자동으로 처리하기 위해서 사용된다.</td>
</tr>
<tr>
<td>분산 처리</td>
<td>여러 서버에서 대량의 데이터를 처리하기 위해 사용된다.</td>
</tr>
<tr>
<td>재시도 및 로깅</td>
<td>데이터 처리 중 오류 발생 시 자동 재시도 및 로기 기능을 지원하기 위해 사용된다.</td>
</tr>
<tr>
<td>데이터 분석</td>
<td>데이터 마이그레이션, 백업 및 복원, 데이터 분석 등 다양한 용도에 사용된다.</td>
</tr>
</tbody></table>
<h3 id="배치-주요-기능">배치 주요 기능</h3>
<table>
<thead>
<tr>
<th>주요 기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Job과 Step을 이용한 배치 프로세스 구현 기능을 제공한다.</td>
<td>배치 처리의 단위 작업(Job)과 작은 단위 작업(Step)을 이용하여 배치 프로세스를 구현하는 기능을 제공한다.</td>
</tr>
<tr>
<td>Chunk 지향 처리 방식을 통한 대용량 데이터 처리를 지원한다.</td>
<td>Chunk 단위로 데이터를 처리하여 대용량 데이터 처리 기능을 제공한다.</td>
</tr>
<tr>
<td>Quartz, Cron 등의 스케쥴러를 이용한 배치 작업 스케줄링을 제공한다.</td>
<td>스케줄러를 이용하여 배치 작업을 주기적으로 실행하도록 스케줄링 제공한다.</td>
</tr>
<tr>
<td>Batch 작업에 대한 로깅과 예외 처리 기능을 제공한다.</td>
<td>Batch 작업 수행 중 로깅과 예외 처리를 지원하여 안정적인 배치 작업을 제공한다.</td>
</tr>
</tbody></table>
<h4 id="chunk-지향-처리-방식">Chunk 지향 처리 방식</h4>
<ul>
<li>대용량 데이터를 처리하는 데 있어서 &#39;메모리 사용량을 최소화&#39;하고 대용량 데이터를 작은 Chunk 단위로 나누어 처리&#39;하는 방식을 의미한다.</li>
</ul>
<p><strong>Chunk</strong>란 <strong><em>여러 개의 아이템을 묶은 하나의 덩어리, 블록을 의미</em></strong>한다.
한번에 하나씩 아이템을 입력 받아 Chunk 단위의 덩어리로 만든 후 Chunk 단위로 트랜잭션을 처리한다.</p>
<h4 id="quartz">Quartz</h4>
<ul>
<li>Java 기반의 오픈 소스 &#39;스케줄링 라이브러리&#39;이며 특정 시간에 작업을 자동으로 실행하도록 예약을 할 수 있도록 기능을 제공한다.</li>
</ul>
<h4 id="cron">Cron</h4>
<ul>
<li>Cron은 리눅스와 유닉스 계열 운영체제에서 사용되는 예약 작업 &#39;스케줄링 시스템&#39;이다. 특정 시간에 실행되어야 하는 작업을 정의하는 데 사용된다.</li>
</ul>
<hr>
<h3 id="전체적인-진행-과정">전체적인 진행 과정</h3>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/69856900-60aa-408f-a5e4-6faf234f8b68/image.png" alt=""></p>
<ul>
<li>Job Launcher로 Job을 실행한다. (Job Launcher -&gt; Job)</li>
<li>Job은 Step을 통해 실제 배치처리를 수행한다. (Job -&gt; Step)</li>
<li>Step에서 읽어오고(Item Reader) -&gt; 처리하고(Item Processor) -&gt; 저장(Item Writer)을 수행한다.</li>
</ul>
<hr>
<h4 id="job">Job</h4>
<blockquote>
</blockquote>
<p><strong>Job</strong>이란 하나 이상의 Step으로 구성되며 &#39;배치 처리의 최상위 단위&#39;를 의미한다.</p>
<ul>
<li>실행 시점에 파라미터를 전달받을 수 있으며 실행 결과를 반환할 수 있다.</li>
<li>1개의 Job은 1개 이상의 Step으로 구성되어 있다.</li>
</ul>
<h4 id="-job의-수행과정">* Job의 수행과정</h4>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/f6d92b65-1c06-4226-b7b5-bab90959f1c9/image.png" alt=""></p>
<h4 id="-jobparameters가-포함된-수행과정">* JobParameters가 포함된 수행과정</h4>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/bddfafee-6fcd-4baa-9f12-641bb9bea026/image.png" alt=""></p>
<h4 id="수행과정에서-나온-용어-정리📒">수행과정에서 나온 용어 정리📒</h4>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Job</td>
<td>배치 처리의 ‘단위 작업’을 의미한다.</td>
</tr>
<tr>
<td>JobInstance</td>
<td>하나의 Job 실행을 나타내는 ‘인스턴스’를 의미한다.</td>
</tr>
<tr>
<td>JobParameters</td>
<td>Job 실행 시 필요한 ‘파라미터’를 의미한다.</td>
</tr>
<tr>
<td>JobRepository</td>
<td>Job 실행 정보를 저장하고 관리하는 ‘저장소’를 의미한다.</td>
</tr>
<tr>
<td>JobLauncher</td>
<td>Job을 실행하는 인터페이스를 의미한다.</td>
</tr>
<tr>
<td>JobExecution</td>
<td>Job 실행 정보를 나타내는 인스턴스를 의미한다.</td>
</tr>
<tr>
<td>JobExecutionListener</td>
<td>Job 실행 전후로 수행할 작업을 정의하는 인터페이스를 의미한다.</td>
</tr>
</tbody></table>
<h4 id="step">Step</h4>
<blockquote>
</blockquote>
<p><strong>Step</strong>이란 실제 배치 처리를 수행하는 단위를 의미하며 Spring Batch Job안에는 한 개 이상의 Step으로 구성되어 있다.</p>
<ul>
<li>Step에서는 하나의 작업만 처리를 수행하는 <strong><em>Tasklet 방식</em></strong>과 Reader-Processor-Writer 묶음으로 여러 작업을 처리를 수행하는 <strong><em>Chunk 방식</em></strong>이 있다.
<img src="https://velog.velcdn.com/images/kkong_do/post/77dd894c-f015-4bb7-a716-d93b1f34d229/image.png" alt=""></li>
</ul>
<h4 id="step에서-사용되는-용어-정리📒">Step에서 사용되는 용어 정리📒</h4>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Step</td>
<td><strong>Job을 구성하는 작은 단위 작업</strong>을 의미한다.</td>
</tr>
<tr>
<td>ItemReader</td>
<td><strong>데이터를 읽어오는 인터페이스</strong>를 의미한다.</td>
</tr>
<tr>
<td>ItemProcessor</td>
<td><strong>읽어온 데이터를 처리하는 인터페이스</strong>를 의미한다.</td>
</tr>
<tr>
<td>ItemWriter</td>
<td><strong>처리한 데이터를 출력하는 인터페이스</strong>를 의미한다.</td>
</tr>
<tr>
<td>ExecutionContext</td>
<td><strong>Step 실행 중 필요한 컨텍스트 정보</strong>를 의미한다.</td>
</tr>
<tr>
<td>StepExecution</td>
<td><strong>Step 실행 정보를 나타내는 인스턴스</strong>를 의미한다.</td>
</tr>
<tr>
<td>StepExecutionListner</td>
<td><strong>Step 실행 전후로 수행할 작업을 정의하는 인터페이스</strong>를 의미한다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="참고-사이트">참고 사이트</h3>
<ul>
<li><a href="https://spring.io/batch">https://spring.io/batch</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#domainLanguageOfBatch">https://docs.spring.io/spring-batch/reference/#domainLanguageOfBatch</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#job">https://docs.spring.io/spring-batch/reference/#job</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#configureJob">https://docs.spring.io/spring-batch/reference/#configureJob</a></li>
<li><a href="https://docs.spring.io/spring-batch/reference/#remote-chunking">https://docs.spring.io/spring-batch/reference/#remote-chunking</a></li>
<li><a href="https://adjh54.tistory.com/169">https://adjh54.tistory.com/169</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱Spring Querydsl]]></title>
            <link>https://velog.io/@kkong_do/Spring-Query-DSL</link>
            <guid>https://velog.io/@kkong_do/Spring-Query-DSL</guid>
            <pubDate>Mon, 08 Jul 2024 10:41:39 GMT</pubDate>
            <description><![CDATA[<h2 id="querydsl">Querydsl</h2>
<blockquote>
</blockquote>
<p>** Querydsl<strong>이란 정적 타입을 이용하여 SQL과 같은 쿼리를 생성할 수 있도록 지원하는 *</strong><u>라이브러리를 의미한다.</u>*** 
즉 SQL 형식의 쿼리를 Type-Safe하게 생성할 수 있도록 DSL을 제공하는 라이브러리인 것이다.</p>
<ul>
<li>여기서 <strong>&#39;dsl&#39;</strong>이라는 용어는 특정 도메인에서 발생하는 문제를 효과적으로 해결하기 위해 설계된 언어이다.</li>
<li><code>@Query</code>를 사용해서 직접 JPQL의 쿼리를 보통 작성할 것이다. 
하지만 직접 문자열을 입력하기 때문에 컴파일 시점에서 에러를 잡아내지 못한다.</li>
<li><strong>Querydsl의 주요 장점 중 하나로서 컴파일 시점에서 쿼리 오류를 잡아낼 수 있다.</strong></li>
<li>IDE가 제공하는 코드 자동 완성 기능을 사용할 수 있다.</li>
<li><strong>주의점</strong> : Spring Boot 2에서 3로 올라오면서 javax가 jakarta로 변경되었다.</li>
</ul>
<hr>
<h2 id="querydsl-사용방법">Querydsl 사용방법</h2>
<h3 id="1-querydsl---jpa-의존성-추가">1. Querydsl - JPA 의존성 추가</h3>
<pre><code class="language-java">implementaion &#39;com.querydsl:querydsl-jpa:5.0.0:jakarata&#39;</code></pre>
<p>해당 코드를 추가하는데, querydsl-jpa 버전 명시 뒤에 :jakarta 추가 꼭 하기!!</p>
<h3 id="2-qclass-생성을-위한-annotaionprocessor-추가">2. QClass 생성을 위한 annotaionProcessor 추가</h3>
<pre><code class="language-java">    annotationProcessor &quot;com.querydsl:querydsl-apt:${dependencyManagement.importedProperties[&#39;querydsl.version&#39;]}:jakarta&quot;
    annotationProcessor &quot;jakarta.annotation:jakarta.annotation-api&quot;
    annotationProcessor &quot;jakarta.persistence:jakarta.persistence-api&quot;</code></pre>
<ul>
<li>QClass란 엔티티 클래스 속성과 구조를 설명해주는 메타데이터를 의미한다. Type-Safe하게 쿼리 조건 설정이 가능하다.</li>
</ul>
<h3 id="3-buildgradle내-querydsl-세팅-하기">3. build.gradle내 querydsl 세팅 하기</h3>
<pre><code class="language-java">// querydsl 세팅 시작
def querydslDir = &quot;$buildDir/generated/querydsl&quot;

sourceSets {
    main.java.srcDir querydslDir
}
configurations {
    querydsl.extendsFrom compileClasspath
}</code></pre>
<details>
<summary>📜build.gradle 전체 코드 📜</summary>
<div>

<pre><code class="language-java">buildscript {
    ext {
        queryDslVersion = &quot;5.0.0&quot;
    }
}

plugins {
    id &#39;java&#39;
    id &#39;org.springframework.boot&#39; version &#39;3.3.0&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.1.5&#39;
}

group = &#39;com.example&#39;
version = &#39;0.0.1-SNAPSHOT&#39;

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    compileOnly &#39;org.projectlombok:lombok&#39;
    developmentOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
    runtimeOnly &#39;com.mysql:mysql-connector-j&#39;
    annotationProcessor &#39;org.springframework.boot:spring-boot-configuration-processor&#39;
    annotationProcessor &#39;org.projectlombok:lombok&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
    testRuntimeOnly &#39;org.junit.platform:junit-platform-launcher&#39;

    implementation &#39;com.querydsl:querydsl-jpa:5.0.0:jakarta&#39;
    annotationProcessor &quot;com.querydsl:querydsl-apt:${dependencyManagement.importedProperties[&#39;querydsl.version&#39;]}:jakarta&quot;
    annotationProcessor &quot;jakarta.annotation:jakarta.annotation-api&quot;
    annotationProcessor &quot;jakarta.persistence:jakarta.persistence-api&quot;
}

tasks.named(&#39;test&#39;) {
    useJUnitPlatform()
}
// querydsl 세팅 시작
def querydslDir = &quot;$buildDir/generated/querydsl&quot;

sourceSets {
    main.java.srcDir querydslDir
}
configurations {
    querydsl.extendsFrom compileClasspath
}</code></pre>
</div>
</details>

<hr>
<h3 id="4-db와-연동하는-방법">4. DB와 연동하는 방법</h3>
<ul>
<li>application.properties에 다음 코드를 작성한다.</li>
</ul>
<pre><code class="language-java">spring.application.name=QueryDsl-tutorial
spring.datasource.url=jdbc:mysql://localhost:3306/querydsl
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username={나의 사용자 이름}
spring.datasource.password={나의 사용자가 접근할 비밀번호}
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

logging.level.org.springframework.data.jpa.repository.query=DEBUG
logging.level.com.querydsl.jpa.impl.JPAQueryFactory=DEBUG</code></pre>
<hr>
<h3 id="5-entity-클래스-만들기">5. Entity 클래스 만들기</h3>
<pre><code class="language-java">package com.springboot.querydsltutorial.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String name;
    @Column(nullable = false)
    private String email;
    @OneToMany(mappedBy = &quot;user&quot;)
    @JsonManagedReference
    private List&lt;Post&gt; posts;
}
</code></pre>
<pre><code class="language-java">package com.springboot.querydsltutorial.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonBackReference
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;
}
</code></pre>
<p>여기까지 잘 따라왔으면 build를 한번 실행해보자!
그렇게 되면 Q도메인 클래스가 생기는 걸 확인할 수 있다.
<img src="https://velog.velcdn.com/images/kkong_do/post/80a346fe-05d3-4c96-81ce-d21e0d314200/image.png" alt=""></p>
<p>나는 User 엔티티와 Post 엔티티를 만들었기 때문에 QUser 클래스, QPost 클래스가 만들어 졌다.</p>
<blockquote>
<p><strong>Q도메인 클래스</strong>는 JPA 엔티티를 기반으로 생성되어 해당 엔티티의 필드와 관계를 정적으로 나타내는 java 클래스다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/89f54728-1eeb-489c-8f03-522beed1ec65/image.png" alt="">
<img src="https://velog.velcdn.com/images/kkong_do/post/577f7098-953a-4dbc-955e-3a491e4485b0/image.png" alt=""></p>
<ul>
<li>Q도메인 클래스를 사용하면 쿼리를 작성할 때 문자열 기반의 필드명이나 테이블명을 사용하는 것보다 타입 안정성을 제공 받을 수 있다. </li>
</ul>
<hr>
<h3 id="6-jpaqueryfactory-빈을-등록하기">6. JPAQueryFactory 빈을 등록하기</h3>
<pre><code class="language-java">package com.springboot.querydsltutorial.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QueryDSLConfiguration {
    @PersistenceContext
    EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}
</code></pre>
<ul>
<li>QueryDSL 사용을 위한 Factory를 Bean으로 등록한다.</li>
<li>JPAQueryFactory는 QueryDSL에서 제공하는 주요 클래스 중 하나이며, 해당 Config 파일을 만들어 JPAQueryFactory를 QueryDSL을 이용한 JPA 쿼리를 빌드하는 Factory 역할로서 사용할 수 있도록 Bean으로 등록해야 한다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>@PersistenceContext</strong>
JPA에서 사용하며 Entity Manager를 필드나 메서드 매개변수로 주입할 때 사용한다. 
Entity Manager는 JPA에서 영속성 컨텍스트를 관리하므로 트랜잭션 관리, 데이터베이스와 상호작용(CRUD) 등을 지원한다. </p>
<hr>
<h3 id="7-사용할-레포지터리-선언하기">7. 사용할 레포지터리 선언하기</h3>
<ul>
<li>Repository는 총 3개를 만들어야 한다.<ol>
<li><strong>JpaRepository(Interface)</strong></li>
<li>직접 커스텀한 <strong>CustomRepository(Interface)</strong></li>
<li>직접 커스텀한 CustomRepository(Interface)를 구현한 <strong>CustomRepositoryImpl(Class)</strong>구현체를 사용하게 된다.</li>
</ol>
</li>
</ul>
<ol>
<li><strong>JpaRepository(Interface)</strong><pre><code class="language-java">// UserRepository로서 JpaRepository랑 UserRepositoryCustom을 상속받는다. 
// (둘 다 인터페이스!!)
@Repository
public interface UserRepository extends JpaRepository&lt;User, Long&gt;, UserRepositoryCustom {
}</code></pre>
</li>
<li><strong>CustomRepository(Interface)</strong><pre><code class="language-java">package com.springboot.querydsltutorial.repository;
</code></pre>
</li>
</ol>
<p>import com.springboot.querydsltutorial.entity.User;</p>
<p>import java.util.List;</p>
<p>// 우리가 사용할 메서드를 선언해준다.
public interface UserRepositoryCustom {
    List<User> findUsersByName(String name);
    List<User> findUsersByNameAndEmail(String name, String email);
}</p>
<pre><code>3. **CustomRepository(Class)**
```java
// 우리는 해당 CustomRepository를 이용해서 QueryDsl을 사용할 수 있게 되고, 동적 쿼리 또한 만들 수 있다.
package com.springboot.querydsltutorial.repository;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.springboot.querydsltutorial.entity.QPost;
import com.springboot.querydsltutorial.entity.QUser;
import com.springboot.querydsltutorial.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.List;

import static com.springboot.querydsltutorial.entity.QPost.post;

@Repository
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
    private final JPAQueryFactory queryFactory;

    @Override
    public List&lt;User&gt; findUsersByName(String name) {
        // RequestParam으로 받아온 이름을 조건식에 사용해서 해당 조건에 맞는 엔티티 가져오기
        QUser user = QUser.user;
        QPost post = QPost.post;
        return queryFactory.select(user).from(user)
                .leftJoin(user.posts, post).fetchJoin()
                .where(user.name.eq(name))
                .fetch();
    }

    @Override
    public List&lt;User&gt; findUsersByNameAndEmail(String name, String email) {
        QUser user = QUser.user;
        QPost post = QPost.post;
        BooleanBuilder builder = new BooleanBuilder();
        // BooleanBuilder는 다중 조건 처리를 할 때, &#39;()&#39;가 들어가는 상황에서 좋고
        // BooleanExpression은 단순한 쿼리나 단일 조건일 때

        if (name != null &amp;&amp; !name.isEmpty()) {
            builder.and(user.name.eq(name));
        }
        if( email != null &amp;&amp; !email.isEmpty()) {
            builder.and(user.email.eq(email));
        }
        return queryFactory.selectFrom(user)
                .leftJoin(user.posts, post).fetchJoin()
                .where(builder)
                .fetch();
    }
}
</code></pre><ul>
<li>동적 쿼리를 사용하기 위해서는 <code>BooleanBuilder</code>를 사용한다. 즉
BooleanBuilder를 이용해 조건절을 추가한 뒤 where절에 전달해서 동적으로 구현해 낼 수 있다.</li>
<li>JPAQueryFactory를 선언할 때 Entity의 타입을 명시해주지 않아도 되고 <code>selectFrom()</code>부터 시작한다. 만약 전체가 아니라 일부만 조회하고 싶다면 <code>.select().from()</code>과 같이 분리해서 작성하면 된다. 또한, select()에 <code>select(a,b,c)</code>와 같이 조회하고 싶은 일부만 뽑아낼 수 있다. </li>
<li>return 받으려면 <code>fetch()</code> 메서드를 사용해야 한다. 
한 건의 조회 경우 <code>fetchOne()</code>, 여러 건의 조회 결과 중 1건 반환은 <code>fetchFirst()</code>, 조회 결과의 개수는 <code>fetchCount()</code>, 조회 결과 리스트와 개수를 포함한 것은 <code>fetchResults()</code>를 사용하면 된다.</li>
</ul>
<h4 id="booleanbuilder와-booleanexpression의-차이점"><code>BooleanBuilder</code>와 <code>BooleanExpression</code>의 차이점</h4>
<ul>
<li><p><code>BooleanBuilder</code></p>
<ul>
<li><strong>가변 객체</strong>이며, 조건을 동적으로 추가하거나 제거할 수 있다는 점이 존재한다. 그러므로 상황에 따라 조건을 조합해야하는 동적 쿼리에 더 유용하게 사용할 수 있다.</li>
</ul>
</li>
<li><p><code>BooleanExpression</code></p>
<ul>
<li><strong>불변 객체</strong>이며, 한번 설정된 조건을 변경할 수 없다는 점이 존재한다. 그러므로 새로운 조건이 추가되면 새로운 <code>BooleanExpression</code>을 생성해야된다.</li>
</ul>
</li>
</ul>
<h4 id="expressionsnumbertemplate">Expressions.numberTemplate</h4>
<ul>
<li>Expressions.numberTemplate은 Querydsl에서 제공하는 메소드 중 하나로, SQL 템플릿 표현식을 사용하여 쿼리의 특정 부분을 정의할 때 사용된다.</li>
</ul>
<hr>
<h3 id="7-service단-만들기">7. Service단 만들기</h3>
<pre><code class="language-java">package com.springboot.querydsltutorial.service;

import com.springboot.querydsltutorial.dto.UserResponseDto;
import com.springboot.querydsltutorial.entity.User;
import com.springboot.querydsltutorial.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    @Transactional(readOnly = true)
    public List&lt;UserResponseDto&gt; findUsersByName(String name) {
        List&lt;User&gt; userList = userRepository.findUsersByName(name);
        List&lt;UserResponseDto&gt; userResponseDtoList = userList.stream().map(UserResponseDto::of).collect(Collectors.toList());
        return userResponseDtoList;
    }
    @Transactional(readOnly = true)
    public List&lt;UserResponseDto&gt; findUsersByNameAndEmail(String name, String email) {
        List&lt;User&gt; users = userRepository.findUsersByNameAndEmail(name, email);
        List&lt;UserResponseDto&gt; userResponseDtoList = users.stream().map(UserResponseDto::of).collect(Collectors.toList());
        return userResponseDtoList;
    }
}</code></pre>
<p>Service단에서는 그냥 UserRepository와 같이 JpaRepository와 CustomRepository (현재 코드상에서는 UserRepository)를 상속받은 인터페이스를 DI받아서 사용하면 된다.</p>
<hr>
<h3 id="8-controller단에서-데이터-뽑아내기">8. Controller단에서 데이터 뽑아내기</h3>
<pre><code class="language-java">package com.springboot.querydsltutorial.controller;

import com.springboot.querydsltutorial.dto.UserResponseDto;
import com.springboot.querydsltutorial.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping(&quot;/users&quot;)
    public List&lt;UserResponseDto&gt; getUsersByName(@RequestParam(&quot;name&quot;) String name) {
        return userService.findUsersByName(name);
    }

    @GetMapping(&quot;/users/search&quot;)
    public List&lt;UserResponseDto&gt; getUsersByNameAndEmail(@RequestParam(required = false) String name,
                                                        @RequestParam(required = false) String email) {
        return userService.findUsersByNameAndEmail(name, email);
    }
    // /users/search : 모든 사용자를 반환한다.
    // /users/search?name=안녕 : 이름이 안녕인 사용자의 데이터를 모두 반환한다.
    // /users/search?email=hello@example.com : 이메일이 hello@example.com인 사용자의 데이터를 모두 반환한다.
    // /users/search?name=안녕&amp;email=hello@example.com : 이름이 &quot;안녕&quot;이고 이메일이 &quot;hello@example.com&quot;인 사용자를 반환한다.
}
</code></pre>
<h3 id="결과-실제-구현한-코드의-rest-api🔫">결과. 실제 구현한 코드의 REST API🔫</h3>
<p><strong>/users?name=안녕</strong> : 이름이 안녕인 사용자의 데이터를 모두 반환한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/df0433a8-9bdd-4b75-8c10-7f9e8dfa6c01/image.png" alt=""></p>
<h4 id="동적-쿼리를-사용한-get-method">동적 쿼리를 사용한 GET Method</h4>
<p>** /users/search?name=안녕** : 이름이 안녕인 사용자의 데이터를 모두 반환한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/aa2c9ee0-2787-4bd6-898a-774f2e6ca88e/image.png" alt=""></p>
<p><strong>/users/search?email=email@naver.com</strong> : 이메일이 <a href="mailto:email@naver.com">email@naver.com</a>인 사용자의 데이터를 모두 반환한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/1e2c58c9-5156-43a2-b6f4-833cfc282dbb/image.png" alt="">
<strong>/users/search?name=안녕&amp;email=email@naver.com</strong> : 이름이 &quot;안녕&quot;이고 이메일이 &quot;<a href="mailto:email@naver.com">email@naver.com</a>&quot;인 사용자를 반환한다.
<img src="https://velog.velcdn.com/images/kkong_do/post/28d637d7-877d-4992-bdc9-9ed31662a210/image.png" alt=""></p>
<hr>
<h2 id="querydsl-그럼-언제-사용할까🧐">Querydsl, 그럼 언제 사용할까?🧐</h2>
<ul>
<li>가독성 향상, 메서드 네이밍을 통해 쿼리 조건, 정렬 방식을 유추할 수 있다.</li>
<li><em><strong>고정된 SQL 쿼리를 작성하지 않고 동적으로 쿼리를 생성할 수 있다.</strong></em></li>
<li>메서드 분리를 통한 재사용성이 향상된다.</li>
<li><em><strong>Type-Safe한 Q클래스를 통한 문법으로 인해서 런타임 에러를 방지할 수 있다.</strong></em></li>
</ul>
<blockquote>
</blockquote>
<p>즉 간단하게 정리하자면 <strong>동적으로 조건문을 주고 싶을때</strong>, <strong>런타임 에러를 방지</strong>하고 싶을때 사용한다.
(Ex. 필터를 걸어서 검색을 하는 경우)</p>
<ul>
<li>하지만 1차 캐시의 장점을 누릴 수 없다. (해당 단점은 JPQL의 특징에도 포함된다.)</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱Spring Data JPA]]></title>
            <link>https://velog.io/@kkong_do/Spring-Data-JPA</link>
            <guid>https://velog.io/@kkong_do/Spring-Data-JPA</guid>
            <pubDate>Sun, 16 Jun 2024 13:38:56 GMT</pubDate>
            <description><![CDATA[<h2 id="jpa와-hibernate">JPA와 Hibernate</h2>
<h3 id="jpa">JPA</h3>
<blockquote>
</blockquote>
<p><strong>JPA</strong>(Java Persistence API)는 자바 진영의 ORM(Object-Relational Mapping) 기술 표준으로 채택된 인터페이스(Interface)의 모음이다.</p>
<p>여기서 우리는 <strong>&#39;ORM&#39;</strong>이라는 단어를 간단하게 살펴보면 객체 지향 언어에서 의미하는 <strong><em>객체(클래스)와 RDB의 테이블을 자동으로 매핑(Mapping)하는 방법</em></strong>을 의미한다.</p>
<p>객체 지향 프로그래밍에서는 데이터를 객체 형태로 다루고, RDB는 데이터를 테이블 형태로 저장한다. 이 두 시스템 간에는 다음과 같은 불일치가 존재한다.</p>
<h4 id="object와-rdb의-존재하는-불일치">Object와 RDB의 존재하는 불일치</h4>
<ul>
<li><strong>상속</strong> : 객체 지향 언어에서는 상속을 통해 계층 구조를 만들 수 있지만, RDB에는 상속 개념이 없다.</li>
<li><strong>연관 관계</strong> : 객체 간의 연관 관계(1:1, 1,N 등)를 표현하는 방법이 데이터베이스의 외래 키와는 다르다.</li>
<li><strong>식별자</strong> : 객체는 주로 참조를 통해 식별되지만, 데이터베이스는 주로 기본 키를 통해 식별된다.</li>
<li><strong>데이터 타입</strong> : 객체와 데이터베이스의 데이터 타입이 다를 수 있다.</li>
</ul>
<p>=&gt; <u>즉. 이러한 둘 사이간의 불일치성을 해결하는 역할이 바로 <strong>ORM</strong>이다.</u></p>
<p>JPA는 이러한 ORM을 구현하기 위한 표준 인터페이스를 제공하며, JPA를 구현한 대표적인 구현체로는 다음과 같이 세가지들이 있다.</p>
<h4 id="jpa의-구현체의-종류">JPA의 구현체의 종류</h4>
<ul>
<li>EclipseLink</li>
<li><strong><em>Hibernate</em></strong> 
(가장 널리 사용되는 JPA 구현체로, 다양한 기능과 높은 호환성을 자랑한다.)</li>
<li>DataNucleus</li>
</ul>
<h3 id="hibernate">Hibernate</h3>
<blockquote>
</blockquote>
<p><strong>Hibernate(하이버네이트)</strong>는 자바의 ORM 프레임워크로서, JPA가 정의하는 인터페이스를 구현하고 있는 JPA 구현체 중 하나이다.</p>
<p>Hibernate는 객체와 관계형 데이터베이스 간의 매핑을 관리하며, 데이터베이스와의 상호작용을 간소화한다. 또한, Query문들을 콘솔창에서 확인하고 싶으면, 다음과 같은 설정을 application.properties 파일에 추가하면 된다.</p>
<pre><code class="language-java">// 쿼리 로그 Show를 true로 설정
// 실행되는 SQL 쿼리를 콘솔에 출력한다.
spring.jpa.show-sql=true 

// SQL문을 정렬하여 출력
// 즉, 출력되는 SQL 쿼리를 보기 쉽게 포맷팅한다.
spring.jpa.properties.hibernate.format_sql=true 


// 바인딩되는 파라미터 값을 출력
logging.level.org.hibernate.type.descriptor.sql=trace</code></pre>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/bfa7b7df-cc60-4cf7-bfa9-18991e9d0168/image.png" alt=""></p>
<hr>
<h2 id="spring-data-jpa">Spring Data JPA</h2>
<p>JPA를 편리하게 사용할 수 있도록 지원하는 스프링 하위 프로젝트 중 하나이다. CRUD 처리에 필요한 인터페이스를 제공하고 Hibernate의 엔티티 매니저를 직접 다루지 않고도 Repository를 정의해 사용함으써 <strong><em>스프링이 적합한 쿼리를 동적으로 생성하는 방식으로 DB를 조작한다.</em></strong>
<img src="https://velog.velcdn.com/images/kkong_do/post/f29e1c0a-64d0-4304-aeea-7c1edd6eeb25/image.png" alt=""></p>
<h3 id="spring-data-jpa의-주요-기능">Spring Data JPA의 주요 기능</h3>
<p>이번 글에서 다루는 Spring Data JPA 기능은 다음과 같다.</p>
<ul>
<li>JPQL(Java Persistence Query Language)</li>
<li>Query Method</li>
<li>정렬과 페이징 처리</li>
<li>@Query annotation</li>
<li>QueryDSL</li>
</ul>
<hr>
<h3 id="jpqljava-persistence-query-language">JPQL(Java Persistence Query Language)</h3>
<blockquote>
</blockquote>
<p><strong>JPA에서 사용할 수 있는 쿼리</strong>를 의미한다.</p>
<p>JPQL의 문법이 SQL 문법과 매우 비슷하여 데이터베이스 쿼리에 익숙한 분들이 어렵지 않게 사용할  수 있다. JPQL은 엔티티 객체를 대상으로 수행하는 쿼리이므로 매핑된 엔티티의 이름과 필드의 이름을 사용한다.</p>
<pre><code class="language-java">// JPQL Basic Example
SELECT P FROM PRODUCT P WHERE P.NUMBER = ?1;

// PRODUCT는 엔티티 타입, NUMBER는 엔티티 객체의 속성을 의미한다.
// 1은 첫 번째 파라미터 의미한다. (여기서는 매개변수로 받은 number의 값이 들어갈 것이다.)</code></pre>
<h3 id="query-method">Query Method</h3>
<blockquote>
</blockquote>
<p>리포지터리에서 기본으로 제공되는 메서드외 별도의 메서드를 정의해야 하는 경우 쿼리문을 작성하기 위해 사용되는 것이 바로 <strong><em>Query Method</em></strong>이다.</p>
<p>우리가 사용하는 Repository 인터페이스는 기본적으로 JpaRepository를 구현받아서 다양한 CRUD 메서드를 제공한다. Query Method는 크게 동작을 결정하는 <strong>Subject(주제) + Predicate(동작)</strong>으로 구분한다. </p>
<p><strong><em>&#39;findBy...&#39;와 &#39;getBy...&#39; 등이 Subject(주제)</em></strong>를 나타내며, <strong><em>By는 Predicate(동작)의 시작을 나타내어 구분자의 역할</em></strong>을 한다.</p>
<pre><code class="language-java">// 리포지터리의 쿼리 메서드 생성 예
List&lt;Person&gt; findByLastNameAndEmail(String lastName, String Email);

// 메서드 명에 들어가있는 By 이후의 LastName과 Email이 메서드의 매개변수로 들어가 있다.</code></pre>
<p>서술어에 들어갈 엔티티의 속성 식은 엔티티에서 관리하는 필드만 참조할 수 있다.</p>
<h4 id="query-method의-주제-키워드">Query Method의 주제 키워드</h4>
<blockquote>
</blockquote>
<p>동사와 By 사이의 도메인이 표현될 수 있지만 repository에서 이미 설정한 후이기 때문에 생략 가능하다.</p>
<ol>
<li><strong>조회하는 기능</strong>을 수행하는 키워드
```java</li>
</ol>
<ul>
<li>findBy...</li>
<li>readBy...</li>
<li>getBy...</li>
<li>queryBy...</li>
<li>searchBy...</li>
<li>streamBy...</li>
</ul>
<pre><code>- return type으로 Collection이나 Stream에 속한 하위 타입을 설정할 수 있다.

2. ** 특정 데이터 존재 유무**를 확인하는 키워드
```java
- existsBy...

// Example
boolean existsByNumber(Long number);</code></pre><ul>
<li>return type으로 boolean을 사용한다.</li>
</ul>
<ol start="3">
<li>** 삭제 쿼리를 수행**하는 키워드
```java</li>
</ol>
<ul>
<li>deleteBy...</li>
<li>removeBy...</li>
</ul>
<p>// Example
void deleteByNumber(Long number);
long removeByName(String name);</p>
<pre><code>- return type으로 void나 정수형(삭제된 갯수)을 반환한다.

4. ** 쿼리를 통해 조회된 결과값의 개수를 제한**하는 키워드
```java
- findFirst숫자By...
- findTop숫자By...

// Example
List&lt;Product&gt; findFirst5ByName(String name);
List&lt;Product&gt; findTop10ByName(String name);</code></pre><ul>
<li>두 키워드는 동일한 동작을 수행한다.</li>
<li>주제와 By사이에 위치하며, 한 번의 동작으로 여러 건을 조회할 때 사용한다.</li>
<li>하나의 건으로 조회하는 것은 숫자를 생략하면 된다.</li>
</ul>
<h4 id="query-method의-조건자-키워드">Query Method의 조건자 키워드</h4>
<blockquote>
</blockquote>
<p>Spring Data JPA의 <strong>메소드 이름 기반 쿼리 작성 방식에서 조건식을 뜻하는 단어를 포함해서 사용되는 키워드</strong>를 의미한다.</p>
<ul>
<li>책에서의 애매한 표현 : _&#39;JPQL의 서술어 부분에서 사용할 수 있는 키워드&#39;_라고 나와있는데 JPQL의 서술어 부분에서 사용할 수 있는 키워드는 JPQL 쿼리의 WHERE 절에서 사용할 수 있는 조건자를 의미한다.</li>
</ul>
<ol>
<li>** 값의 일치를 조건으로 사용**하는 조건자 키워드
```java</li>
</ol>
<ul>
<li>..IS</li>
</ul>
<p>// Example
Product findByNumberIS(Long number);
Product findByNumberEquals(Long number);</p>
<pre><code>
2. ** 값의 불일치를 조건으로 사용**하는 조건자 키워드
```java
- ..(Is)Not

// Example
Product findByNumberIsNot(Long number);
Product findByNumberNot(Long number);</code></pre><ul>
<li>Is 생략하고 Not키워드만 사용할 수 있다.</li>
</ul>
<ol start="3">
<li>** 값이 Null인지 검사**하는 조건자 키워드
```java</li>
</ol>
<ul>
<li>..(Is)Null</li>
<li>..(Is)NotNull</li>
</ul>
<p>// Example
List<Product> findByUpdatedAtNull();
List<Product> findByUpdatedAtIsNull();
List<Product> findByUpdatedAtNotNull();
List<Product> findByUpdatedAtIsNotNull();</p>
<pre><code>4. ** boolean타입으로 지정된 컬럼값을 확인**하는 키워드
```java
- ..(Is)True
- ..(Is)False

// Example
Product findByActiveTrue();
Product findByActiveIsTrue();
Product findByActiveFalse();
Product findByActiveIsFalse();</code></pre><ol start="5">
<li>** 여러 조건을 묶을 때 사용**하는 키워드
```java</li>
</ol>
<ul>
<li>And</li>
<li>Or</li>
</ul>
<p>// Example
Product findByNumberAndName(Long number, String name);
Product findByNumberOrName(Long number, String name);</p>
<pre><code>
6. ** 숫자나 datetime 컬럼을 대상으로 하는 비교 연산 시 사용**하는 키워드
```java
- ..(Is)GreaterThan
- ..(Is)LessThan
- ..(Is)Between

// Example
List&lt;Product&gt; findByPriceGreaterThan(Long price);
List&lt;Product&gt; findByPriceLessThan(Long price);
List&lt;Product&gt; findByPriceBetween(Long startPrice, Long endPrice);List&lt;Product&gt; findByPriceLessThanEqual(Long startPrice, Long endPrice);</code></pre><ul>
<li><code>GreaterThan</code>과 <code>LessThan</code> 키워드는 비교 대상에 대한 초과/미만의 개념으로 비교 연산을 수행한다.</li>
<li>경계값까지 포함시키고 싶으면 <code>Equal</code> 키워드를 추가하면 된다.</li>
</ul>
<ol start="7">
<li>** 컬럼값에서 일부 일치 여부를 확인**하는 키워드
```java</li>
</ol>
<ul>
<li>..(Is)StartingWith</li>
<li>..(Is)EndingWith</li>
<li>..(Is)Containing</li>
<li>..(Is)Like</li>
</ul>
<p>// Example
List<Customer> findByFirstNameStartingWith(String prefix);
List<Customer> findByLastNameEndingWith(String suffix);
List<Customer> findByFirstNameContaining(String infix);
List<Customer> findByLastNameLike(String pattern);</p>
<p>// Service단에서 사용되는 예
List<Customer> customers = customerRepository.findByFirstNameStartingWith(&quot;Jo&quot;);
// John, Johnny, Joanna
List<Customer> customers = customerRepository.findByLastNameEndingWith(&quot;son&quot;);
// Johnson, Jackson, Harrison
List<Customer> customers = customerRepository.findByFirstNameContaining(&quot;ann&quot;);
// Joanna, Annabelle, Hannah
List<Customer> customers = customerRepository.findByLastNameLike(&quot;%son%&quot;);
// Johnson, Jackson, Harrison</p>
<pre><code>- SQL 쿼리문에서 값의 일부를 포함하는 값을 추출할 때 사용하는 &#39;%&#39;와 동일한 역할을 하는 키워드이다.
- 자동으로 생성되는 SQL문을 보면 StartingWith 키워드는 문자열 앞, EndingWith 키워드는 문자열 끝, Containing 키워드는 문자열의 양끝에 &#39;%&#39;가 자동으로 배치된다.
- 하지만! Like키워드는 명시적으로 &#39;%&#39;를 입력해야 한다.

***

### 정렬과 페이징 처리

#### Query Method를 통한 정렬 처리
```java
// 쿼리 메서드의 정렬 처리
// Asc : 오름차순, Desc : 내림차순
List&lt;Product&gt; findByNameOrderByNumberAsc(String name);
List&lt;Product&gt; findByNameOrderByNumberDesc(String name);</code></pre><ul>
<li>기본 쿼리 메서드(findByName)를 작성한 후 <code>OrderBy</code> 키워드를 삽입하여 정렬하고자 하는 컬럼(Number)과 오름차순/내림차순(Asc 혹은 Desc)을 설정하면 정렬이 수행된다.</li>
<li><code>List&lt;Product&gt; findByNameOrderByNumberAsc(String name);</code> 의 Query Method를 해석하면 <strong>&#39;상품 정보를 이름으로 검색한 후 상품 번호로 오름차순 정렬을 수행한다.&#39;</strong>는 의미이다.</li>
<li><code>List&lt;Product&gt; findByNameOrderByNumberDesc(String name);</code> 의 Query Method를 해석하면 <strong>&#39;상품 정보를 이름으로 검색한 후 상품 번호의 내림차순 정렬을 수행한다.&#39;</strong>는 의미이다.</li>
</ul>
<pre><code class="language-java">// 쿼리 메서드에서 여러 정렬 기준 사용
// And를 붙이지 않음
List&lt;Prodcut&gt; findByNameOrderByPriceAscStockDesc(String name);
List&lt;Product&gt; findByNameOrderByPrice(String name);</code></pre>
<pre><code class="language-java">// List&lt;Prodcut&gt; findByNameOrderByPriceAscStockDesc(String name)의 hibernate
Hibernate: 
    select
        product0_.id as id1_0_,
        product0_.name as name2_0_,
        product0_.price as price3_0_,
        product0_.stock as stock4_0_
    from
        product product0_
    where
        product0_.name=?
    order by
        product0_.price asc,
        product0_.stock desc
</code></pre>
<ul>
<li>위처럼 정렬 키워드를 삽입해서 정렬을 수행하는 것도 가능하지만 메서드의 이름이 길어질수록 가독성이 떨어지는 문제가 발생할 수 있다.</li>
<li>그렇기 때문에 ** Sort 객체를 매개변수로 주어 가독성을 보완하는 방법** 또한 있다.</li>
</ul>
<pre><code class="language-java">List&lt;Product&gt; findByName(String name, Sort sort);</code></pre>
<pre><code class="language-java">// Service단에서 indByName을 호출하는 예시
productRepository.findByName(&quot;연필&quot;, Sort.by(Order.asc(&quot;price&quot;)));
productRepository.findByName(&quot;볼펜&quot;, Sort.by(Order.asc(&quot;price&quot;), Order.desc(&quot;stock&quot;)));</code></pre>
<ul>
<li>Sort 클래스는 내부 클래스로 정의되어 있는 Order 객체를 활용해 정렬 기준을 세우고, Order 객체 내 있는 <code>asc()</code> 메서드와 <code>desc()</code> 메서드를 활용하여 오름차순과 내림차순을 지정한다.</li>
<li>여러 정렬 기준을 사용할 경우에는 <code>,(콤마)</code>를 이용하여 구분한다.</li>
<li>Sort 클래스를 호출한 위치에서 정렬 기준이 길어져 가독성이 떨어지는 문제점이 발생할 수 있다.</li>
<li>그렇기에 호출하는 부분에서 하나의 메서드로 분리해서 Query method를 호출하는 코드를 작성하는 방법 또한 있다.</li>
</ul>
<pre><code class="language-java">class ProductService{
...
productRepository.findByName(&quot;연필&quot;, getSort());
...
private Sort getSort(){ // 필요한 쿼리메서드를 하나의 메서드로 분리하여 코드를 재활용 츠견을 높였다.
    return Sort.by(
        Order.Asc(&quot;price&quot;),
        Order.Desc(&quot;stock&quot;)
    );
}</code></pre>
<h4 id="페이징-처리">페이징 처리</h4>
<blockquote>
</blockquote>
<p><strong>페이징(Paging)</strong>이란 데이터베이스의 레코드를 개수로 나눠 페이지를 구분하는 것을 의미한다. 이를 통해 많은 양의 데이터를 효율적으로 처리하고, 사용자에게 필요한 부분만을 보여줄 수 있다.</p>
<ul>
<li>JPA에서는 Page와 Pageable 인터페이스를 사용하여 페이징을 구현한다.<pre><code class="language-java">// 페이징 처리를 위한 쿼리 메서드
Page&lt;Product&gt; findByName(String name, Pageable pageable);</code></pre>
</li>
<li>return type으로 Page를 설정하고, parameter에는 pageable 타입의 객체를 정의하였다.</li>
<li><strong>Page</strong>: 페이징된 결과를 포함하는 객체로, 조회된 데이터 리스트와 함께 페이지 정보(총 페이지 수, 현재 페이지 번호, 총 레코드 수 등)를 제공한다.</li>
<li><strong>Pageable</strong>: 페이징 정보를 담고 있는 객체로, 페이지 번호와 페이지 크기 등을 설정할 수 있다.<pre><code class="language-java">// Service단에서 indByName을 호출하는 예시
Page&lt;Product&gt; productPage = produtRepository.findByName(&quot;연필, PageRequest.of(0, 2));
for(Product product : productPage.getContent()){
  System.out.println(product);
}
</code></pre>
</li>
</ul>
<p>// 출력결과예시
// Product{id=1, name=&#39;연필&#39;, price=100.0, stock=50}
// Product{id=2, name=&#39;연필&#39;, price=120.0, stock=30}</p>
<pre><code>```java
Hibernate: 
    select
        product0_.id as id1_0_,
        product0_.name as name2_0_,
        product0_.price as price3_0_,
        product0_.stock as stock4_0_
    from
        product product0_
    where
        product0_.name=? limit ?
Hibernate: 
    select
        count(product0_.id) as col_0_0_
    from
        product product0_
    where
        product0_.name=?</code></pre><ul>
<li>PageRequest는 Pageable의 구현체이다. 다시 말해 Pageable 객체는 PageRequest를 사용하여 생성한다.</li>
<li>limit 절은 결과로 반환될 행(row)의 수를 제한하는 데 사용된다.</li>
<li>Page 객체에서 제공하는 <code>getContent()</code> 메서드를 사용하여 엔티티의 리스트를 가져올 수 있다.</li>
</ul>
<h5 id="pageable-of-메서드의-종류">Pageable of() 메서드의 종류</h5>
<table>
<thead>
<tr>
<th>of 메서드</th>
<th>매개변수 설명</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>of(int page, int size)</td>
<td>페이지 번호(0부터 시작), 페이지당 데이터 갯수</td>
<td>데이터 정렬X</td>
</tr>
<tr>
<td>of(int page, int size, Sort)</td>
<td>페이지 번호(0부터 시작), 페이지당 데이터 갯수, 정렬</td>
<td>sort에 의해 정렬</td>
</tr>
<tr>
<td>of(int page, int size, Direction, String --- 속성)</td>
<td>페이지 번호(0부터 시작), 페이지당 데이터 갯수, 정렬 방향, 속성(컬럼)</td>
<td>Sort.by(direction, properties)에 의해 정렬</td>
</tr>
</tbody></table>
<hr>
<h4 id="여기까지-잘-따라왔으면-한가지-의문점이-들-것이다❓">여기까지 잘 따라왔으면 한가지 의문점이 들 것이다❓</h4>
<blockquote>
<p>🙋🏻‍♂️ : &quot;OK, 그럼 그냥 JPQL(혹은 native SQL)을 작성하지 않고 Query Method만 작성하면 되는거야??&quot;
📢 : <strong>&quot;음.. 그렇게도 생각 할 수 있어 하지만 아냐!!&quot;</strong> </p>
</blockquote>
<ul>
<li>우선 <strong>Spring Data JPA는 메서드 이름을 분석하여 자동으로 쿼리를 생성하는 기능을 제공하며 이를 _<u>메서드 쿼리 생성(Method Query Creation)</u>_</strong>이라고 한다!! </li>
<li>이 기능을 통해 우리는 특정 패턴을 따르는 메서드 이름을 정의함으로써 복잡한 쿼리를 작성할 필요 없이 데이터베이스 작업을 간편하게 수행할 수 있다. -&gt; <del>_<u>&#39;쉽죠? 아 쉽죠는 어려운 말의 반대말입니다. 깔깔&#39;</u>_</del></li>
</ul>
<ul>
<li><strong>그러나!! BUT!! However!! Nevertheless!!</strong> 
복잡한 조건을 가진 쿼리나 여러 테이블을 조인해야 하는 경우, 특정 데이터베이스의 고유한 함수나 기능을 사용해야 하는 경우, 여러 조건을 동적으로 추가해야 하는 경우 Query Method로 구현하기가 어렵다.</li>
<li><strong>=&gt; @Query 어노테이션을 사용하여 직접 쿼리문을 작성해주면 된다.*</strong><pre><code class="language-java">// Example
package com.example.firstobject.repository;
</code></pre>
</li>
</ul>
<p>import com.example.firstobject.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;</p>
<p>import java.util.List;</p>
<p>@Repository
public interface CommentRepository extends JpaRepository&lt;Comment, Long&gt; {
    // 특정 게시글의 모든 댓글 조회
      @Query(&quot;SELECT p FROM Product p WHERE p.name = :name&quot;)
    List<Product> findByName(@Param(&quot;name&quot;) String name);
    // 특정 닉네임의 모든 댓글 조회
    List<Comment> findByNickname(String nickname);
}</p>
<pre><code>- 이러한 간편한 기능 덕분에 메서드 이름만으로 간단한 쿼리를 생성할 수 있도 있고 필요에 따라 직접 쿼리를 작성하여 복잡한 요구 사항을 충족할 수 있다.

***

### @Query annotation
&gt;
@Query 어노테이션은 JPQL 또는 네이티브 SQL을 직접 작성하여 튜닝된 쿼리를 사용할 때 사용하는 어노테이션이다.

- 기본적으로 Spring Data JPA는 메서드 이름을 기반으로 JPQL을 자동으로 생성한다. 하지만 개발자가 직접 작성한 JPQL 또는 네이티브 SQL을 사용하고 싶다면 `@Query` 어노테이션을 사용할 수 있다.

```java
public interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {

    // @Query 어노테이션을 사용한 사용자 정의 JPQL 쿼리
    @Query(&quot;SELECT p FROM Product p WHERE p.name = :name&quot;)
    List&lt;Product&gt; findByNameCustom(@Param(&quot;name&quot;) String name);

    // @Query 어노테이션을 사용한 네이티브 SQL 쿼리
    @Query(value = &quot;SELECT * FROM product WHERE name = :name&quot;, nativeQuery = true)
    List&lt;Product&gt; findByNameNative(@Param(&quot;name&quot;) String name);
}</code></pre><h4 id="native-sql-vs-jpql">native SQL VS JPQL</h4>
<table>
<thead>
<tr>
<th></th>
<th>native SQL</th>
<th>JPQL</th>
</tr>
</thead>
<tbody><tr>
<td><strong>대상</strong></td>
<td>데이터베이스 테이블</td>
<td>엔티티 객체</td>
</tr>
<tr>
<td><strong>데이터베이스 독립성</strong></td>
<td>데이터베이스 종속적</td>
<td>데이터베이스 독립적</td>
</tr>
<tr>
<td><strong>구문</strong></td>
<td>SQL 구문 직접 사용</td>
<td>객체 지향적인 구문</td>
</tr>
<tr>
<td><strong>유연성 및 최적화</strong></td>
<td>데이터베이스의 고유 기능 사용</td>
<td>객체 지향적인 접근 방식</td>
</tr>
</tbody></table>
<ul>
<li>JPQL은 <code>FROM</code> 절 뒤에 엔티티 타입을 지정하고 별칭을 설정한다.</li>
<li><code>WHERE</code> 절을 통해 SQL과 마찬가지로 조건을 지정하는데 <code>?1</code>, <code>?2</code>와 같이 순번을 이용해서 인자를 받아올 수도 있다. <pre><code class="language-java">// 순번을 이용해서 인자를 받아 올 수 있는 Example
@Query(&quot;SELECT p FROM Product p WHERE p.name = ?1&quot;)
List&lt;Product&gt; findByName(String name);</code></pre>
</li>
<li>하지만 파라미터의 순서가 바뀔 수 있기 때문에 <code>@Param</code>을 어노테이션을 사용하여 파라미터를 직접 바인딩하는 방식으로 메소드를 구현하면 오류 발생 확률을 줄이고 유지보수를 수월하게 할 수 있다.</li>
<li>마지막으로, <code>@Query</code> 어노테이션은 엔티티 타입이 아니라 원하는 컬럼의 값만 추출할 수도 있고 이때의 리턴 타입은 <code>List&lt;Object[]&gt;</code>형태로 지정할 수 있다.</li>
</ul>
<hr>
<h4 id="용어-정리🤷🏻♂️">용어 정리🤷🏻‍♂️</h4>
<ul>
<li>** 스프링 하위 프로젝트** : Spring 프레임워크의 기능을 확장하고 특정 용도를 위해 더 쉽게 사용할 수 있도록 제공하는 추가 모듈 또는 라이브러리이다. </li>
<li>** 엔티티 매니저 ** : JPA(Java Persistence     API)에서 중요한 역할을 하는 인터페이스로, 애플리케이션이 데이터베이스와 상호 작용할 수 있도록 하는 주요 객체이다. 엔티티의 생명 주기를 관리하고, 데이터베이스 쿼리를 수행하며, 트랜잭션을 관리한다.</li>
</ul>
<hr>
<p><strong>Reference📚</strong></p>
<ul>
<li>스프링부트 핵심 가이드 - 장정우 -</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱Spring Framework와 Spring Boot]]></title>
            <link>https://velog.io/@kkong_do/Spring-Framework%EC%99%80-Spring-Boot</link>
            <guid>https://velog.io/@kkong_do/Spring-Framework%EC%99%80-Spring-Boot</guid>
            <pubDate>Tue, 11 Jun 2024 10:34:15 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<h3 id="스프링의-핵심가치💡">스프링의 핵심가치💡</h3>
<p>애플리케이션 개발에 필요한 기반을 제공해서 <strong><em>개발자가 비즈니스 로직 구현에만 집중할 수 있게끔 하는 것</em></strong></p>
<h2 id="spring-framework">Spring Framework</h2>
<ul>
<li><p>엔터프라이즈급 애플리케이션을 개발하기 위한 다양한 기능을 제공하는 <strong>자바 기반의 애플리케이션 프레임워크</strong></p>
</li>
<li><p>즉 &#39;오픈소스 경량급 애플리케이션 프레임워크&#39; 라고 부른다.</p>
</li>
</ul>
<hr>
<h2 id="spring-boot">Spring Boot</h2>
<blockquote>
</blockquote>
<p>단독으로 실행 가능한 상용 수준의 스프링 기반 애플리케이션을 손쉽게 만들 수 있도록 도와주는 프레임워크</p>
<ul>
<li>스프링 프레임워크에서 필요한 모듈들을 설정하는 복잡한 과정을 해결하기 위해서 나온 것이 바로 <strong>*<u>스프링 부트</u>*</strong>이다.</li>
</ul>
<h3 id="스프링-부트의-특징-스프링-프레임워크와-비교하였을-때">스프링 부트의 특징 (스프링 프레임워크와 비교하였을 때)</h3>
<ul>
<li>의존성 관리</li>
<li>자동 설정</li>
<li>내장 WAS</li>
<li>모니터링</li>
</ul>
<h4 id="의존성-관리">의존성 관리</h4>
<ul>
<li>기존 Spring Framework에서는 개발에 필요한 각 모듈의 의존성을 직접 설정해야 했지만 스프링 부트는 Spring-boot-starter를 통해 자주 사용하는 라이브러리와 의존성을 쉽게 추가하고 관리한다.</li>
</ul>
<h4 id="자동-설정">자동 설정</h4>
<ul>
<li>애플리케이션을 개발하는데 필요한 의존성을 추가하면 프레임워크가 알아서 자동으로 관리해준다.</li>
</ul>
<h4 id="내장-was">내장 WAS</h4>
<ul>
<li>톰캣, 제티, 언더토우 등의 내장 서버를 포함하여 별도의 서버 설치 없이 애플리케이션을 실행할 수 있다.</li>
</ul>
<h4 id="모니터링">모니터링</h4>
<ul>
<li>서비스를 운영하는 시기에 해당 시스템이 사용하는 스레드, 메모리, 세션 등의 주요 요소들을 모니터링하는데 이러한 모니터링을 쉽게 하기 위해 제공하는 Spring Boot Actuator라는 자체 모니터링 도구가 있다.</li>
</ul>
<hr>
<h4 id="reference📚">Reference📚</h4>
<ul>
<li>스프링 프레임워크 첫걸음 - 후루네스 키노시타 마사아키 -</li>
<li>스프링부트 핵심 가이드 - 장정우 -</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git이란? 그리고 어떻게 사용하는가?]]></title>
            <link>https://velog.io/@kkong_do/Git%EC%9D%B4%EB%9E%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@kkong_do/Git%EC%9D%B4%EB%9E%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Wed, 08 May 2024 01:19:56 GMT</pubDate>
            <description><![CDATA[<h2 id="git">Git</h2>
<blockquote>
</blockquote>
<p><strong><em>Git이란?</em></strong> 
로컬에서 코드를 효율적으로 관리하기 위해서 사용되는 <strong><em>분산 버전 관리 도구</em></strong>로서 코드의 다양한 버전을 관리, 변경 히스토리를 추적, 변경사항을 안전하게 원격 저장소에 저장하는 기능을 제공한다.</p>
<h3 id="일반적으로-파일을-새롭게-만들면서-하면-되는데-git을-왜-사용하나요🤔">일반적으로 파일을 새롭게 만들면서 하면 되는데 Git을 왜 사용하나요?🤔</h3>
<ul>
<li>일반적으로 파일을 새롭게 만들면서 하는 것은 중복된 내용이 계속 쌓여가기에 용량면으로 비효율적이므로 자원이 낭비된다.<blockquote>
<p>하지만 <strong><em>Git을 사용하게 되면 Diff(변경사항)을 기반으로 내용만이 쌓여가기에 용량면으로 효율적</em></strong>이므로 자원을 아낄 수 있다.</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="git-사용방법">Git 사용방법</h2>
<ul>
<li>Git을 사용하면 <strong><em>1. 로컬 코드를 관리</em></strong>할 수 있으며 나아가 <strong><em>2. Github에 자신이 만든 코드를 공유</em></strong>할 수 있거나 <strong><em>3. 동료 개발자가 만든 코드를 이어서 작성</em></strong>할 수 있다.</li>
</ul>
<h3 id="git-시작하기-2가지-방법">Git 시작하기 2가지 방법</h3>
<ul>
<li><ol>
<li>Remote에서 시작 &lt;= 평상시 개발할 때 많이 사용하는 방법</li>
</ol>
</li>
<li><ol start="2">
<li>Local에서 시작</li>
</ol>
</li>
</ul>
<h4 id="1-remote에서-시작하는-방법">1. Remote에서 시작하는 방법</h4>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/95fe1617-24e0-42da-89af-8f49201bd93e/image.jpg" alt=""></p>
<ul>
<li><code>git clone</code> : remote 저장소에서 local 저장소로 파일을 가져온다.</li>
</ul>
<h4 id="2-local에서-시작하는-방법">2. Local에서 시작하는 방법</h4>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/cc7f76ae-d408-4b1a-b5e6-70a8b150e41c/image.jpg" alt=""></p>
<ul>
<li><code>git init</code> : <strong><em>현재 작업중인 디렉터리로 git으로 관리하겠다고 선언해준다</em></strong></li>
<li><code>git branch -m main</code> : 브랜치명을 main으로 변경한다.</li>
<li><code>git remote add origin 원격저장소의 보안통신주소</code> : 업로드한 Remote를 연결하기 위해 원격저장소의 보안통신주소를 지정한다.</li>
<li><code>git push -u origin main</code> : 로컬 브랜치 main에 있는 파일들을 remote저장소에 있는 origin에 push한다.</li>
</ul>
<hr>
<h2 id="local에서-git-branch-및-remote-저장소-주소-기본-설정-방법">Local에서 Git Branch 및 Remote 저장소 주소 기본 설정 방법</h2>
<h3 id="local에서-branch를-관리하는-방법">Local에서 Branch를 관리하는 방법</h3>
<ul>
<li><ol>
<li>Local에서 Branch를 설정하는 방법</li>
</ol>
</li>
<li><ol start="2">
<li>Remote에서 Branch를 가져오는 방법</li>
</ol>
</li>
</ul>
<h4 id="1-local에서-branch를-설정하는-방법">1. Local에서 Branch를 설정하는 방법</h4>
<ul>
<li><strong>Read(조회)</strong><ul>
<li><code>git branch -r</code> : remote : Remote(원격) Branch 조회 </li>
<li><code>git branch -l</code> : local : Local(로컬) Branch 조회<ul>
<li><code>git branch -a</code> : all : Remote Branch + Local Branch 전체 조회</li>
</ul>
</li>
</ul>
</li>
<li><strong>Create(추가)</strong><ul>
<li><code>git checkout -b 로컬저장소명</code> : Local Branch 생성</li>
</ul>
</li>
<li><strong>Delete(삭제)</strong><ul>
<li><code>git branch --delete 혹은 -D 브랜치명</code> : Local Branch 삭제</li>
<li><code>git push --delete 혹은 -D origin 원격저장소에 있는 브랜치명</code> : Remote Branch 삭제</li>
<li><code>origin</code> : 원격 주소에 대한 Alias 별명, 별칭</li>
<li><code>원격저장소에 있는 브랜치명</code> : 삭제하고자 하는 브랜치명</li>
</ul>
</li>
</ul>
<h4 id="2-remote에서-branch를-가져오는-방법">2. Remote에서 Branch를 가져오는 방법</h4>
<blockquote>
<p><code>git fetch</code>와 <code>git fetch -p</code>는 원격 저장소에 변경사항을 로컬 저장소에 가져오는 명령이라는 점에서는 똑같지만 약간의 차이가 있다.</p>
</blockquote>
<ul>
<li><code>git fetch</code> : 원격 브랜치의 최신 상태를 로컬의 원격 추전 브랜치(Ex orgin/main)에 업데이트만 하고 현재 작업중인 브랜치의 작업 내용은 변하지 않는다. 
= 단순히 데이터 업데이트만 하고 로컬 브랜치에 자동으로 병합하지 않는다.</li>
<li><code>git fetch -p</code> : <code>-p</code> 명령을 사용하여 원격 저장소에 삭제된 브랜치에 해당하는 로컬의 원격 추적 브랜치를 자동으로 삭제한다. 
= 원격 저장소에서 더이상 존재하지 않는 브랜치 정보를 로컬에서도 정리한다. </li>
</ul>
<h3 id="local에서-remote-저장소를-관리하는-방법">Local에서 Remote 저장소를 관리하는 방법</h3>
<ul>
<li><p><strong>Read(조회)</strong></p>
<ul>
<li><code>git remote -v</code> : verbose : 원격 저장소 상세 출력</li>
</ul>
</li>
<li><p><strong>Create(추가)</strong></p>
<ul>
<li><code>git remote add origin 원격저장소의 보안통신주소</code></li>
<li><code>origin</code> : 원격 주소에 대한 Alias 별명, 별칭</li>
<li><code>원격저장소의 보안통신주소</code> : 원격 저장소</li>
</ul>
</li>
<li><p><strong>Update(수정)</strong></p>
<ul>
<li><code>git remote set-url origin 새로 연결할 원격저장소의 보안통신주소</code></li>
</ul>
</li>
<li><p><strong>Delete(삭제)</strong></p>
<ul>
<li><code>git remote remove origin</code> : 원격 저장소 연결 해제</li>
</ul>
</li>
</ul>
<hr>
<h2 id="local에서-git과-githubremote-동기화">Local에서 Git과 Github(Remote) 동기화</h2>
<blockquote>
<p>Pull를 통해 Local과 Remote와의 동기화를 진행한다.
<strong><em>Pull = Fetch + Merge</em></strong>로 이루어진 명령어</p>
</blockquote>
<ul>
<li><strong>Fetch</strong> : 원격(Remote) github 저장소에서 생성/삭제된 브랜치를 가져오고 최신 코드 모두 가져온다.</li>
<li><strong>Merge</strong> : 로컬에 가져온 &quot;원격 브랜치의 최신 코드&quot;를 &quot;로컬 브랜치의 구식 코드&quot;에 합치는 작업을 의미한다.<ul>
<li>명령어를 작성하는 곳은 로컬 브랜치에 위치해있어야 하며 <code>git merge</code> 명령어를 사용할 때 merge 뒤에 원격 저장소의 Alias를 작성해주면 된다. 아니면 <code>git merge fetch-head</code>를 사용해도 된다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="local에서-gitlocal과-githubremote-동기화-시-conflict-충돌해결-방법">Local에서 Git(Local)과 Github(Remote) 동기화 시 Conflict 충돌해결 방법</h2>
<ul>
<li>방법 1. <strong>Rebase</strong> : 현재 나의 작업물의 ** 1. Base(기준이 되는)를 다시 재설정<strong>한 뒤, ** 2. 다시 커밋을 쌓는다.</strong>
단점으로 내가 작업했던 커밋들이 다시 생성되기에, 히스토리가 리셋된다.</li>
<li>방법 2. <strong>Merge</strong> : 현재 나의 작업물과 Remote에 업로드되어 있는 상대 작업물 모두 존중하고, 머지 커밋을 만든다.
단점으로 머지 커밋이 생성되어서 머지 수가 많아짐에 따라 커밋이 더러워진다.
=&gt; <strong>Merge 방법을 주로 사용한다.</strong></li>
</ul>
<h3 id="충돌발생-시-입력해야-하는-명령어-순서">충돌발생 시 입력해야 하는 명령어 순서</h3>
<blockquote>
<ol>
<li><code>git pull</code> -&gt; 2. <code>git status</code> -&gt; 3. 해당 충돌난 파일들을 수정한다. -&gt; 4. <code>git add .</code> - &gt; 5. <code>git commit</code> -&gt; 6. <code>git push</code></li>
</ol>
</blockquote>
<ul>
<li><code>git pull</code> 명령어를 통해 Remote 저장소의 최신 상태를 가져오고 내 로컬 저장소와 병합한다.</li>
<li><code>git status</code> 명령어에서 충돌난 파일들이 무슨 파일인지 확인한다.</li>
<li><code>git add .</code> 명령어를 통해 staging 영역으로 보내준다.</li>
</ul>
<hr>
<h2 id="git-reset-명령어">Git reset 명령어</h2>
<ul>
<li>** 스테이징된 파일만 취소하고 싶을때**<pre><code class="language-bash">git reset</code></pre>
</li>
<li>** 특정 커밋으로 HEAD를 이동시키고 스테이징된 변경 사항을 취소하고 싶을 때**<pre><code class="language-bash">git reset &lt;commit&gt;</code></pre>
</li>
<li>** 특정 커밋으로 HEAD를 이동시키고, 스테이징된 변경 사항과 워킹 디렉토리의 변경 사항을 모두 삭제하고 싶을 때 **<pre><code class="language-bash">git reset --hard &lt;commit&gt;</code></pre>
</li>
<li>** 특정 커밋으로 HEAD를 이동시키고, 인덱스와 워킹 디렉토리를 유지하고 싶을 때 **<pre><code class="language-bash">git reset --soft &lt;commit&gt;
</code></pre>
</li>
</ul>
<pre><code>***
### Git branch 이름 변경
- ** Local에 있는 branch 이름 변경하기 **</code></pre><p>git branch -m oldbranch newbranch</p>
<pre><code>- ** Remote에 존재하는 old branch 삭제하기 **</code></pre><p>git push origin :oldbranch</p>
<pre><code>- ** new branch push 하기 **</code></pre><p>git push origin newbranch</p>
<pre><code>***
## Git의 흐름
![](https://velog.velcdn.com/images/kkong_do/post/72d76c1f-b145-435e-8f68-9d4c3a81dcce/image.jpg)
- 위 그림은 로컬 환경에 존재하는 영역들이다.
- P.S) 원격 저장소에서 데이터를 가져오게 되면 원격 저장소의 변경사항이 로컬 작업 디렉터리에 바로 적용이 된다. 
즉 **git pull(혹은 fetch + merge)는 스테이징 영역을 거치지 않고 바로 작업 디렉터리로 들어온다.**

***

## Git Commit 메세지 참고
![](https://velog.velcdn.com/images/kkong_do/post/22b83673-6cd9-46bf-a36f-4f35cd302f0a/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[웹보안 : HTTPS와 웹 브라우저에서의 CORS]]></title>
            <link>https://velog.io/@kkong_do/%EC%9B%B9-%EB%B3%B4%EC%95%88-HTTPS-%EC%99%80-%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C%EC%9D%98-CORS</link>
            <guid>https://velog.io/@kkong_do/%EC%9B%B9-%EB%B3%B4%EC%95%88-HTTPS-%EC%99%80-%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C%EC%9D%98-CORS</guid>
            <pubDate>Tue, 30 Apr 2024 05:51:50 GMT</pubDate>
            <description><![CDATA[<h2 id="https와-cors">HTTPS와 CORS</h2>
<ul>
<li><strong>HTTPS</strong>는 웹 통신 내 <strong>요청, 응답에 대한 암호화</strong>를 진행한다. </li>
<li><strong>CORS</strong>는 웹 브라우저에서 <strong>악의적 웹 요청(CSRF)에 대한 ‘부분적’ 방어 정책</strong>이다.<ul>
<li><strong>‘부분적’의 의미</strong> : 완벽하게 CORS를 방어하지 못한다는 의미이다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="https-http-secured-tls">HTTPS (HTTP Secured, TLS)</h3>
<p>=&gt; 웹 통신 내 End-to-End 회선 보호</p>
<blockquote>
<p>HTTPS는 인터spt 상에서 정보를 암호화하는 SSL 프로토콜을 이용하여 <strong><em>웹브라우저와 웹 서버간의 데이터나 자원을 주고 받는 통신규약</em></strong>을 의미한다.</p>
</blockquote>
<ul>
<li><p>MITM (Man-In-The-Middle) 공격 방지</p>
</li>
<li><p><strong>단일</strong> 서버 : <strong>다수</strong> 클라이언트 (웹 브라우저 혹은 웹 서버)</p>
<ul>
<li><strong>단일</strong> 서버만 사용 가능한 키 : <strong>비공개키(Private Key)</strong></li>
<li><strong>다수</strong> 클라이언트 모두 사용 가능한 키 : <strong>공개키(Public Key)</strong></li>
</ul>
</li>
</ul>
<ul>
<li>HTTPS 는 <strong>대칭키 암호화</strong> 방식과 <strong>비대칭키 암호화</strong> 방식의 장점만을 사용</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kkong_do/post/1d61b86c-088d-4fb6-80ab-a9d684f6a4ef/image.jpg" alt=""></p>
<hr>
<h3 id="csrf-xsrf-cross-site-request-forgery--크로스-사이트에-대한-의도치-않은-요청">CSRF (<strong>XSRF, Cross-Site Request Forgery</strong>) : 크로스 사이트에 대한 의도치 않은 요청</h3>
<blockquote>
</blockquote>
<p>A 사이트를 이용하는 유저가 A 사이트와 전혀 상관없는 B 사이트(크로스사이트)에 의도치 않은 요청을 보내는 것</p>
<blockquote>
</blockquote>
<p>공격자가 사용자가 이미 인증된 웹 애플리케이션에 대해 원하지 않는 요청을 보내는 공격</p>
<ul>
<li>꼭, 웹 브라우저에서만 가능한것뿐만 아니라 네이티브 앱에서도 가능하다.<ul>
<li>웹 브라우저에서 CSRF 형태
= 유저가 의도하지 않은 요청이 <strong>자바스크립트 실행</strong>을 통해 서버에 전송된다. 
=&gt; <strong>서버에 요청을 보내는 자바스크립트 = AJAX</strong>       </li>
</ul>
</li>
</ul>
<p>사용자가 뱅킹 사이트에 로그인한 상태에서 공격자가 만든 맬리셔스 웹사이트를 방문하면, 그 사이트는 사용자의 브라우저를 이용해 뱅킹 사이트에 비밀번호 변경 요청을 보낼 수 있습니다. 이러한 요청은 사용자의 의도와 무관하게 이루어진다.</p>
<hr>
<p>⚠️ CSRF 와 CORS 가 헷갈릴 수 있다. 근데 무엇보다도 여기서 CS__와 CO__는 더 헷갈린다.</p>
<p>CS__ (<strong>Cross-Site</strong>) 와 CO__ (<strong>Cross-Origin</strong>) 차이는 무엇인가</p>
<ul>
<li><strong>Origin 와 Site 의 차이</strong><ul>
<li><strong>Origin</strong> : Scheme + Host Name (Domain Name) + Port<ul>
<li>예시) <a href="https://aaron.com:8080">https://aaron.com:8080</a></li>
</ul>
</li>
<li><strong>Site</strong> :  바로 TLD 다음 도메인 + TLD<ul>
<li>예시) <a href="http://www.hello.com">www.hello.com</a> → 여기서 <strong>Site</strong> = hello.com | <strong>TLD</strong> = .com</li>
<li>eTLD+1 까지 Site 라고 불러줄 수 있다.<ul>
<li><strong>effective TLDs(eTLD)</strong> : mypage.github.io or <a href="http://www.hello.co.kr">www.hello.co.kr</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="corscross-origin-resource-sharing">CORS(Cross-Origin Resource Sharing)</h3>
<blockquote>
</blockquote>
<p>AJAX 통한 크로스사이트 웹 서버에 대한 요청 방어를 위한 웹 브라우저의 정책</p>
<p>** - 웹브라우저<strong>에서 **자바스크립트 AJAX</strong>를 통한 <strong>CSRF (크로스사이트에 대한 악의적 요청) 방지한다.</strong></p>
<ul>
<li><strong>1. 웹브라우저</strong> : <strong>네이티브 앱</strong>에서는 CSRF 방어하기 위해 XSRF Token 사용 (임의 난수 + 세션 활용)</li>
<li><strong>2. 자바스크립트 AJAX</strong> : 웹 브라우저에서 AJAX가 아닌 <strong>FORM</strong>을 통한 POST 요청 방어 불가</li>
</ul>
<h3 id="cors의-유형">CORS의 유형</h3>
<h4 id="cors-1-웹브라우저-비동기-지원-ajax">CORS 1) <strong>웹브라우저 비동기 지원 AJAX</strong></h4>
<ul>
<li><strong>FORM</strong> (<strong>Synchronous</strong>) = 보내고 끝 (<strong>HTML 페이지 반환 = 페이지 리렌더 O</strong>)</li>
<li><strong>AJAX</strong> (<strong>Asynchronous</strong> JavaScript and XML) = XHR (<strong>XML 객체 반환 = 페이지 리렌더 X</strong>)<ul>
<li><strong>비동기</strong> = XHR 객체 활용 시 <strong>서버에 데이터를 요청</strong>하거나, <strong>데이터를 전송받을 수 있다.</strong><ul>
<li>즉, 웹 페이지 전체를 다시 로딩하지 않고 일부분만을 갱신할 수 있게 된다.</li>
<li>XHR (XmlHttpRequest) : W3C 표준이 아님 (브라우저마다 설계 방식 차이)<ul>
<li>현재 대부분의 주요 웹 브라우저는 서버에 데이터를 요청하기 위한 XHR 객체 활용</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="cors-2-웹브라우저-보안-정책-sop-same-origin-policy">CORS 2) 웹브라우저 보안 정책 SOP (Same-Origin Policy)</h4>
<p>웹브라우저에서 <strong>HTTP Resource</strong> 를 갖고오기 위한 모든 HTTP 요청은 <strong>기본적으로 <a href="https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy">SOP</a></strong></p>
<ul>
<li><strong>웹브라우저는 SOP (Same Origin Policy) 정책</strong>을 갖는다.<ul>
<li><strong>Same Origin 의 의미</strong> - 예시) <a href="https://www.hello.com:8080">https://www.hello.com:8080</a><ul>
<li>Scheme = https://</li>
<li>Host = <a href="http://www.hello.com">www.hello.com</a></li>
<li>Port = 8080</li>
</ul>
</li>
<li>하지만 웹브라우저는 이미지, 아이콘처럼 외부에서 정보를 가져오는 경우가 있기 때문에</li>
<li>SOP (Same-Origin Policy) 정책은 ‘Same-Origin’ 이름과 달리 <strong>부분적으로 Cross-Origin 도 허용</strong><ul>
<li><strong>Cross-Origin ’가져오기’ (</strong><code>&lt;img/&gt;</code>, <code>&lt;script/&gt;</code><strong>)</strong> : 의도된 조회 = 서버 상태 변경 불가</li>
<li><strong>Cross-Origin</strong> ’<strong>제출하기’ (FORM)</strong> : 개발자가 설계한 의도된 제출 = 의도된 서버 상태 변경</li>
<li><strong>Cross-Origin ’요청하기’ (AJAX - 예, POST) : 서버 상태 변경 가능 → SOP 에서 불허</strong><ul>
<li>Cross-Origin 에 대한 AJAX 는 <strong>Cross-Origin 서버 상태를 바꿀 수 있는 보안 위험성 존재</strong>            </li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="cors-3-sop-보완-정책--cors-cross-origin-resource-policy">CORS 3) SOP 보완 정책 = CORS (Cross Origin Resource Policy)</h4>
<ul>
<li><p><strong>Cross-Origin ’요청하기’ (AJAX - 예, POST) : 서버 상태 변경 가능 → SOP 에서 불허</strong></p>
<ul>
<li><p>Cross-Origin 에 대한 AJAX 는 <strong>Cross-Origin 서버 상태를 바꿀 수 있는 보안 위험성 존재</strong></p>
<ul>
<li><p>하지만, AJAX 는 표준 기술이 아님에도 사실상 표준으로써 AJAX 를 막게되면 <strong>모든 API 호출 불가</strong></p>
<ul>
<li><p>그래서 결국, SOP 를 보완하여 AJAX 를 부분적으로 허용할만한 추가 정책 필요</p>
<p>  = <strong>AJAX 는 CORS 정책에 맞을시에만 조건부 허용</strong></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>브라우저에서 처리하는것</strong>이기에 서버-서버 통신시에는 CORS 이슈는 전혀 발생하지 않는다.</p>
</li>
</ul>
<h3 id="cors-서버-측-적용-방법">CORS 서버 측 적용 방법</h3>
<p><strong>서버</strong>는 브라우저로부터 어떤 요청만 받을지 <strong>3가지 CORS 헤더 설정</strong>으로 호출가능 요청을 제약한다</p>
<blockquote>
<p>CORS(Cross-Origin Resource Sharing) 정책에서 사용되는 헤더들은 
웹 애플리케이션의 보안을 강화하고, 다른 출처의 리소스 요청을 효과적으로 관리하기 위해 사용된다.</p>
</blockquote>
<ol>
<li><strong>허용된 Origin</strong> (예, a.com)<ul>
<li><strong>Origin</strong> (브라우저) - 브라우저가 요청할때 보낸다.</li>
<li><strong>Access-Control-Allow-Origin</strong> (서버)</li>
</ul>
</li>
</ol>
<p>-
이 헤더는 서버가 어떤 출처(origin)의 요청을 허용할지를 지정한다.
출처는 프로토콜, 호스트, 포트를 포함한 URL이다.
예를 들어, 서버가 Access-Control-Allow-Origin: <a href="https://example.com%EC%9D%84">https://example.com을</a> 설정하면, 오직 <a href="https://example.com%EC%97%90%EC%84%9C">https://example.com에서</a> 온 요청만 서버에 의해 허용된다.
만약 모든 출처에서의 요청을 허용하고자 한다면, 이 헤더를 Access-Control-Allow-Origin: *로 설정할 수 있다. 그러나 보안상의 이유로 모든 요청을 허용하는 것은 권장되지 않는다.</p>
<ol start="2">
<li><strong>허용된 Method</strong> (예, GET, HEAD 만 허용하고 POST 외 제외)<ul>
<li><strong>Access-Control-Request-Method</strong> (브라우저) - 요청 시 보냄</li>
<li><strong>Access-Control-Allow-Method</strong> (서버)    </li>
</ul>
</li>
</ol>
<ul>
<li>이 헤더는 서버가 허용하는 HTTP 메소드(예: GET, POST, DELETE 등)를 지정한다.
예를 들어, 서버가 오직 GET과 POST 요청만을 허용하려면, Access-Control-Allow-Methods: GET, POST로 설정할 수 있다. 
이 설정은 사전 요청(Preflight Request)에 대한 응답에서 특히 중요하며, 실제 요청에서 사용될 메소드를 서버가 승인하는 역할을 한다.</li>
</ul>
<ol>
<li><strong>허용된 Header</strong><ul>
<li><strong>Access-Control-Request-Headers</strong> (브라우저) - 요청 시 보냄</li>
<li><strong>Access-Control-Allow-Headers</strong> (서버)</li>
</ul>
</li>
</ol>
<ul>
<li><p>이 헤더는 서버가 허용하는 특정 HTTP 헤더를 지정한다. 
클라이언트가 요청에 포함할 수 있는 헤더(예: X-Custom-Header, Content-Type 등)를 서버가 명시적으로 허용해야 할 때 사용된다. 
예를 들어, Access-Control-Allow-Headers: X-Custom-Header, Content-Type는 클라이언트가 이 두 헤더를 요청에 포함시킬 수 있음을 의미한다.</p>
</li>
<li><p><strong>추가 : 자격증명 Header 허용</strong></p>
<ul>
<li><strong>allowCredentials = true</strong> (브라우저) - 요청 시 보내야함<ul>
<li>요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션</li>
<li><strong>자격증명</strong> = Cookie, Authorization Headers 또는 TLS 클라이언트 인증</li>
</ul>
</li>
<li><strong>Access-Control-Allow-Credentials</strong> = true (서버)</li>
</ul>
</li>
</ul>
<hr>
<p>브라우저는 서버에서 설정한 위 3가지 CORS 설정을 알기 위해서 <strong>서버에게 호출하여 알아낸다</strong></p>
<ul>
<li><p><strong>Simple Request — 서버 상태 조회 :</strong> GET, HEAD</p>
<ul>
<li><strong>허용된 Origin 체크 절차</strong><ul>
<li>브라우저가 서버에게 원 요청 을 보내면</li>
<li>서버는 “결과”와 함께 “CORS 헤더”를 같이 보내준다.</li>
<li>브라우저는 “CORS 헤더” 가 요청과 부합하지 않으면, 반환 결과 를 폐기한다.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Preflight Request — 서버 상태 변경 :</strong> POST, PATCH, PUT + GET, HEAD w/ 커스텀 Header</p>
<ul>
<li><strong>허용된 Origin 체크 + 허용된 Method 체크 + 허용된 Header 체크 절차</strong><ul>
<li>브라우저가 서버에게 임시 요청 을 보내면 = Preflight (OPTION)</li>
<li>서버는 “CORS 헤더”만 보내준다.</li>
<li>브라우저는 “CORS 헤더” 가 요청과 부합하지 않으면, 원 요청 을 보내지 않는다.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹브라우저와 웹서버의 저장소]]></title>
            <link>https://velog.io/@kkong_do/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%99%80-%EC%9B%B9-%EC%84%9C%EB%B2%84%EC%9D%98-%EC%A0%80%EC%9E%A5%EC%86%8C1</link>
            <guid>https://velog.io/@kkong_do/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%99%80-%EC%9B%B9-%EC%84%9C%EB%B2%84%EC%9D%98-%EC%A0%80%EC%9E%A5%EC%86%8C1</guid>
            <pubDate>Tue, 30 Apr 2024 02:34:09 GMT</pubDate>
            <description><![CDATA[<h2 id="웹-브라우저와-웹-서버의-저장소">웹 브라우저와 웹 서버의 저장소</h2>
<blockquote>
</blockquote>
<p>HTTP is a Stateless Protocol</p>
<ul>
<li><strong>Stateless(불연속성)</strong> <ul>
<li>웹 서버 입장에서 매요청이 웹 브라우저가 보낸 것인지 알 수가 없다.
=&gt; HTTP</li>
</ul>
</li>
<li><strong>Stateful(연속성)</strong> <ul>
<li>웹 서버가 이전에 요청받았던 웹 브라우저와 현재 요청의 웹 브라우저를 구별할 수 있다. 
=&gt; 어떤 유저(웹 브라우저)의 요청인지 알 수 있다면, 요청마다 별개의 작업을 수행하고 결과를 반환할 수 있다. Ex) 로그인 계정 정보</li>
</ul>
</li>
</ul>
<hr>
<h2 id="stateful-http--cookie--session">Stateful HTTP : Cookie &amp; Session</h2>
<p>웹 서버는 웹 브라우저의 요청이 어떤 유저에 의해 요청된 것인지 알기 위해 응답 반환시 요청자 정보를 함께 반환한다.</p>
<ul>
<li><p><strong>웹 브라우저</strong>는 응답을 받아, 응답 헤더에 붙어있던 요청자 정보를 웹 브라우저 쿠키에 저장한다.</p>
</li>
<li><p><strong>웹 브라우저</strong>는 이후 요청부터 웹 서버에게 요청자 정보를 함께 전달하여 웹 서버는 누구의 요청인지 알 수 있다.</p>
<ul>
<li>요청자 정보란? <strong><em>어떤 웹 브라우저(유저)가 요청했는지 인지 가능한 정보</em></strong>를 의미한다.<br><img src="https://velog.velcdn.com/images/kkong_do/post/74b0d337-6c31-40df-9d8e-a5244da878e3/image.jpg" alt=""></li>
</ul>
</li>
<li><p>주의해야할 점은 세션도 쿠키를 전송한다는 점!</p>
</li>
<li><p>웹 서버가 웹 브라우저에게 최초로 전달할 때는 웹 서버 응답(Response)의 헤더(=Set-Cookie)로 전송한다.</p>
</li>
<li><p>웹 브라우저가 그 이후에는 웹 서버로 전송할 때는 웹 브라우저 요청(Request)의 헤더(=Cookie)로 전송한다.</p>
</li>
</ul>
<h3 id="웹-브라우저에-저장하는-것은-cookie-웹-서버에-저장하는-것은-session">웹 브라우저에 저장하는 것은 Cookie, 웹 서버에 저장하는 것은 Session</h3>
<ul>
<li><p><strong>쿠키(Cookie)</strong>는 어떤 값이든 가능하나, 일반적으로 노출 방지를 위해 인간이 이해할 수 있는 형태가 아닌걸로 값들이 저장된다. (Client 측에서)</p>
</li>
<li><p><strong>세션(Seesion)</strong>은 웹 브라우저에 저장할 수 없을 정도로 크거나 복합적인 경우, 웹 브라우저에 저장할 수 없는 민감 정보인 경우 값들이 저장된다. (Server 측에서)</p>
</li>
</ul>
<h2 id="cookie쿠키--웹-브라우저에-저장하는-것1">Cookie(쿠키) : 웹 브라우저에 저장하는 것(1)</h2>
<blockquote>
</blockquote>
<p>Cookie(쿠키)는 웹 브라우저 내 저장되는 값으로서, 
웹 서버의 제어 + 웹 브라우저 내 저장 및 전송하는 값을 의미한다.</p>
<h3 id="쿠키의-사용-기준--domain--path">쿠키의 사용 기준 : Domain + Path</h3>
<blockquote>
<ul>
<li>웹 브라우저가 쿠키를 웹 서버에게 전송하는 기준이 된다.
(해당 쿠키를 어떤 요청에 보낼지)</li>
</ul>
</blockquote>
<ul>
<li>모든 쿠키에는 도메인이 연결되어 있다.</li>
</ul>
<p>Ex) 웹 브라우저내 저장되어 있는 쿠키 리스트</p>
<ul>
<li>1번 쿠키 : (Domain : a.com, Path : *)</li>
<li>2번 쿠키 : (Domain : a.com, Path : /)</li>
<li>3번 쿠키 : (Domain : b.com, Path : /hello)</li>
<li>4번 쿠키 : (Domain : b.com, Path : /world)</li>
</ul>
<p>Ex) 위 리스트 기반으로 아래 웹 서버에 대한 요청에 다라 전송되는 쿠키</p>
<ul>
<li>웹 브라우저에서 <code>a.com/user/name</code> 호출 시 <strong>1번 쿠키 전송</strong></li>
<li>웹 브라우저에서 <code>a.com/</code> 호출 시 <strong>1번 쿠키 + 2번 쿠키 전송</strong></li>
<li>웹 브라우저에서 <code>b.com/hello</code> 호출 시 <strong>3번 쿠키 전송</strong></li>
<li>웹 브라우저에서 <code>c.com/</code> 호출 시 어떤 쿠키도 전송하지 않음</li>
</ul>
<h3 id="쿠키-유효시간--maxage-or-expires">쿠키 유효시간 : MaxAge or Expires</h3>
<ul>
<li>명시되어 있다면 : <strong>Persistent Cookie</strong> (지속쿠키)</li>
<li>명시되어 있지않다면 : <strong>Session Cookie</strong> (세션쿠키)</li>
</ul>
<h3 id="쿠키의-종류">쿠키의 종류</h3>
<blockquote>
</blockquote>
<p><strong>퍼스트파티 쿠키</strong>와 <strong>서드파티쿠키</strong>로 나누어진다.</p>
<h3 id="쿠키-보안--httponly-secure-samesite">쿠키 보안 : <strong>HttpOnly</strong>, <strong>Secure</strong>, <strong>SameSite</strong></h3>
<ul>
<li><p><strong>HttpOnly</strong> : XSS(자바스크립트) 공격에 의한 쿠키 접근을 제어한다.</p>
</li>
<li><p><strong>Secure</strong> : 패킷 탈취(Man-in-the-Middle) 방지를 위해 HTTPS 채널에서만 사용한다.</p>
<ul>
<li><strong>MITM (Man-in-the-Middle)</strong> : 요청(클라이언트)-응답(서버) 사이에서 요청, 응답 탈취한다.</li>
</ul>
</li>
<li><p><strong>SameSite</strong> : 웹 브라우저 주소란에 표시된 도메인과 동일한 도메인에 대한 요청(API)시에만 쿠키를 전송한다.</p>
<ul>
<li><p>동일 사이트 (Same Site) 의 정확한 의미 : <strong><code>www.web.dev</code></strong> 와 <strong><code>static.web.dev</code> 는 Same Site</strong></p>
</li>
<li><p>막지 않는다면 CSRF 공격으로 <strong>크로스 사이트 요청 시 크로스 사이트에 과거에 설정됐던 쿠키가 전송</strong></p>
<p>  =  <strong>크로스 사이트 요청 시, 서드파티(크로스 사이트) 쿠키가 함께 전송된다면 아래의 문제 발생</strong></p>
<ul>
<li><strong>HTTPS 미사용 시</strong> = MITM 로 요청을 가로챈다면 서드파티 쿠키를 외부에서 볼 수 있다</li>
<li><strong>HTTPS 사용 시</strong> = 서드파티 쿠키가 로그인, 인증 정보를 담고있다면 어드민 API 호출도 가능</li>
<li>전체 유저 정보 조회, 대량 삭제 등 권한이 필요한 API 호출이 인증정보 쿠키로 손쉽게 가능</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="cookie의-단점">Cookie의 단점</h3>
<ol>
<li>쿠키 정보가 <strong>웹 브라우저에 저장되어 있다.</strong><ul>
<li><strong>민감 정보</strong>들이 안전하지 않은채로 저장되어있다.</li>
<li>물론 HttpOnly, Secure, SameSite로 방어도 가능하고 저장되는 정보들에 대해 웹 서버만의 키로 암호화해놓고, 볼때마다 복호화해서 보면되기도 한다.<ul>
<li><strong>웹 브라우저간 공유 불가</strong> : 웹 브라우저 단위의 저장소이다보니 지역성 문제<ul>
<li>매 웹 브라우저에서 하는 검색과 같은 행위들이 해당 브라우저에만 저장된다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<ol start="2">
<li>쿠키는 Domain + Path 만 일치한다면 해당 <strong>웹 서버로 모든 쿠키를 담아 보내다보니</strong> 쿠키로 저장하려는 정보량이 많아질수록 요청 크기가 커진다. 
불필요한 Network Overhaed : 비대해질수록 요청 사이즈도 커진다. </li>
</ol>
<ul>
<li>쿠키를 단지 저장소로 써서는 안되는 이유<pre><code>      - 그렇기에 웹 서버에게 알려주지 않아도 되는 정보의 경우 **웹 스토리지 사용을 권장한다.**</code></pre></li>
</ul>
<hr>
<h2 id="storage--웹-브라우저에-저장하는-것2">Storage : 웹 브라우저에 저장하는 것(2)</h2>
<blockquote>
<p><strong><em>Storage는 Cookie, Session 처럼 Stateful HTTP 를 위한 기술은 아니다.</em></strong></p>
</blockquote>
<ul>
<li>HTML5 표준에서 Storage 등장 전에는 웹 브라우저 저장소는 유일하게 Cookie 만을 활용해야 했다.</li>
<li>HTML5 표준에서 Storage 등장 후에는 웹 브라우저에서 아래 두 유즈케이스를 제대로 나눠 활용할 수 있게 된다.</li>
</ul>
<h3 id="웹-브라우저-내-저장소-storage-와-cookie-목적-차이">웹 브라우저 내 저장소 <strong>Storage 와 Cookie 목적 차이</strong></h3>
<ul>
<li><strong>Storage</strong> : 웹 브라우저 저장소<ul>
<li>유저에 의해 변경된 옵션 상태 등 <strong>필요에 따른 조회가 필요할때 (진짜 저장소)</strong><ul>
<li>예) 마지막에 어떤 소셜 계정으로 로그인했는지 저장하고, 1년 뒤 로그인할때 표시하여 알려주기</li>
</ul>
</li>
</ul>
</li>
<li><strong>Cookie</strong> : 웹 서버에게 웹 브라우저가 매번 전달할 특정 정보 전달을 위한 저장(Stateful)<ul>
<li>로그인 인증 정보 등 → <strong>웹 서버가 매번 요청때마다 판단해야 할 정보를 대신 전달</strong></li>
</ul>
</li>
</ul>
<blockquote>
</blockquote>
<p><strong>쿠키(Cookie)</strong>는 HTTP 요청 시 자동으로 서버로 전송이 되며, 사용자 인증과 같이 사용자를 식별할 때 유용하게 사용된다. &lt;= 보안 취약성이 발생할 수 있다.</p>
<blockquote>
</blockquote>
<p><strong>스토리지(Storage)</strong>는 로컬 스토리지와 세션 스토리지로 나뉘면서, 스토리지에 저장되는 데이터는 서버에 전송되지 않는다. &lt;= 보안성이 강화되지만, 클라이언트 사이드에만 사용된다.</p>
<h3 id="웹-브라우저-내-저장소-storage-와-cookie-목적">웹 브라우저 내 저장소 Storage 와 Cookie 목적</h3>
<ul>
<li><p>웹 브라우저에 저장된다는 점에선 Storage와 Cookie는 동일하다.</p>
</li>
<li><p><strong>Cookie</strong> : <strong>웹 서버에게 반복적으로 전달</strong>하기 위한 + <strong>작은 정보</strong> + (<strong>만료 시간을 갖고있고</strong>)</p>
</li>
<li><p><strong>Storage</strong> : <strong>웹 브라우저에서만 사용가능</strong>한 + <strong>큰 정보</strong> + (<strong>만료 시간 없이</strong>)</p>
</li>
</ul>
<h3 id="storage-의-종류">Storage 의 종류</h3>
<ul>
<li>유효시간에 따라 Storage 종류가 나뉘어진다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>Local Storage</strong> : 웹 브라우저 창이 닫혀도 데이터는 영구적으로 저장한다. - 용량 처리를 조심해야 한다. 
<br> <strong>Session Storage</strong> : 웹 브라우저 창이 닫히면 데이터는 삭제된다.</p>
<h3 id="storage-의-종류별-용례">Storage 의 종류별 용례</h3>
<ul>
<li>프론트엔드 개발자가 브라우저에서 무엇인가를 저장한다하면 Storage를 쓰면 된다.<ul>
<li>단발적이지만 중요한 내용은 Session Storage, 길게 저장해도 되는 내용은 Local Storage를 사용한다.</li>
<li>예) 최근에 로그인했던 수단을 표기하기 위해 Local Storage 내 수단 저장</li>
</ul>
</li>
</ul>
<hr>
<h2 id="session세션--웹-서버내-저장되는-것1">Session(세션) : 웹 서버내 저장되는 것(1)</h2>
<blockquote>
</blockquote>
<p><strong>Session</strong>은 웹 브라우저 쿠키에 저장하던 값을 웹 서버측에 저장하기 위해 <strong>별개의 저장소 DB가 필요하다.</strong></p>
<ul>
<li><strong>주의</strong> : Session을 학습할때 Cookie vs Session 구도로 학습하기에 오해하기 쉬운것이<ul>
<li>Session 을 사용한다고, Cookie 를 사용하지 않는건 아니다.</li>
<li>웹 브라우저 내 Cookie 에는 <strong>어떤 세션인지 알기위한 ID 값 저장 필요</strong>하다.</li>
<li>Session 의 대표적인 예는 웹 브라우저로부터 쿠키로 SESSION_ID를 받아 <strong>해당 요청 유저의 정보 조회</strong>를 한다.<ul>
<li><strong>속도가 매우 중요</strong> : API 수천번의 호출마다 Session 조회가 필요하며,</li>
</ul>
</li>
<li>메모리 기반 DB 인 Redis 를 많이 사용한다.<ul>
<li>메모리 중심 인스턴스의 비용 이슈, 확장성 이슈</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="cookie-의-단점-→-session-의-장점">Cookie 의 단점 → Session 의 장점</h3>
<p><strong>Cookie 의 단점</strong>을 살펴보고 이를 뒤집으면, <strong>Session 의 장점</strong>이 된다.</p>
<ul>
<li><strong>쿠키(Cookie)</strong>는 <strong>정보를 웹 브라우저에 저장</strong>된다.<ul>
<li><strong>민감 정보들이 안전하지 않은채로 저장</strong>되어있다. 
(HttpOnly, Secure, SameSite 방어는 가능하나)</li>
<li><strong>웹 브라우저간 공유 불가하다</strong><br>=&gt; 웹 브라우저 단위의 저장소이다보니 지역성 문제,<ul>
<li>한 유저가 PC 방을 돌아다니며 매 웹 브라우저에서 매번 로그인 해야한다.</li>
</ul>
</li>
</ul>
</li>
<li><strong>세션(Session)</strong>은 <strong>정보를 웹 서버측에 저장</strong>된다.<ul>
<li><strong>민감 정보들이 웹 서버측에 안전하게 저장이 가능하다.</strong></li>
<li><strong>웹 브라우저간 공유가 가능하다.</strong></li>
</ul>
</li>
</ul>
<p>쿠키는 Domain + Path만 일치한다면 해당 **웹서버로 모든 쿠키를 담아 보낸다. 그러다보니</p>
<ul>
<li>쿠키로 저장하려는 정보량이 많아질수록 요청 크기가 커진다
=&gt;불필요한 Network Overhaed : 비대해질수록 요청 사이즈도 커진다<br></li>
<li>Session 은 정보를 웹 서버측에 저장하는것이기에, <strong>아무리 많은 정보를 저장하더라도 요청을 방해하지 않는다.</strong>
=&gt;단, 매 요청마다 Session 저장소에 저장된 세션 정보를 조회해야하므로, <strong>빠른 DB 인 Redis 고려할것</strong>            </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web의 구성과 흐름]]></title>
            <link>https://velog.io/@kkong_do/Web%EC%9D%98-%EA%B5%AC%EC%84%B1%EA%B3%BC-%ED%9D%90%EB%A6%84</link>
            <guid>https://velog.io/@kkong_do/Web%EC%9D%98-%EA%B5%AC%EC%84%B1%EA%B3%BC-%ED%9D%90%EB%A6%84</guid>
            <pubDate>Tue, 23 Apr 2024 02:21:55 GMT</pubDate>
            <description><![CDATA[<h2 id="web">Web</h2>
<blockquote>
</blockquote>
<p>Web은 <strong><em>웹브라우저와 웹서버간의 요청과 반환</em></strong>으로 작동</p>
<hr>
<h3 id="웹브라우저">웹브라우저</h3>
<blockquote>
</blockquote>
<p>사용자가 <strong>* 웹서버에 접속하여 웹 페이지를 검색하고 볼 수 있도록 해주는 소프트웨어 *</strong></p>
<ul>
<li>브라우저는 사용자가 URL을 통해 원하는 웹 페이지를 요청하면, 웹서버와 통신하여 해당 페이지의 데이터를 받아서 사용자의 컴퓨터나 모바일 장치에 볼 수 있도록 표시해준다. </li>
</ul>
<h3 id="웹서버">웹서버</h3>
<blockquote>
</blockquote>
<p>인터넷을 통해 정보를 제공하는 소프트웨어 및 하드웨어의 조합 </p>
<ul>
<li>웹 서버는 웹 페이지, 사이트, 앱 또는 미디어파일과 같은 데이터를 저장하고, 사용자의 요청에따라 클라이언트 컴퓨터로 전송한다.</li>
<li>웹 서버 소프트웨어는 HTTP를 사용하여 클라이언트의 요청을 처리하고 필요한 응답을 보낸다.</li>
</ul>
<hr>
<h3 id="아키텍처architecture">아키텍처(Architecture)</h3>
<ul>
<li><p><strong>Monolithic(모놀리딕)</strong> : 하나의 서버가 모든 서비스를 관장한다.</p>
</li>
<li><p><strong>MSA(Microservice Architecture)</strong> : 하나의 서버가 하나의 서비스만을 관장한다.</p>
</li>
<li><p>Monolithic의 단점 : 한쪽 서비스가 문제가 생기면 모든 서버에 영향을 주어 기타 서비스들을 모두 사용 불가하다.</p>
</li>
<li><p>MSA의 장점 : Monolithic의 단점을 보완해주는데 하나의 서버, 하나의 서비스로 구성되어있어 문제가 발생해도 다른 서버 문제로 전파되지 않는다는 장점이 있다.</p>
</li>
</ul>
<hr>
<h2 id="rest-api">REST API</h2>
<blockquote>
</blockquote>
<p>REST API(Representational State Transfer)는 웹 리소스를 효율적으로 처리하고 사용하기 위한 기술</p>
<ul>
<li><p>Method + URI라는 2가지 요소로 구성된다.</p>
</li>
<li><p><strong>URL</strong>, Location : <a href="https://naver.com">https://naver.com</a> = 장소</p>
</li>
<li><p><strong>URI</strong>, Indicator : <a href="https://naver.com/store/">https://naver.com/store/</a> = 장소 내 지정</p>
</li>
<li><p>URI(Path)에 들어가는 변수</p>
<ul>
<li>Path Variable : /hello/world</li>
<li>Query Parameter : /hello?next=world</li>
</ul>
</li>
</ul>
<pre><code class="language-java">package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    private final RestTemplate restTemplate;

    @Autowired
    public UserController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping(&quot;/info&quot;)
    public ResponseEntity&lt;String&gt; getUserInfo() { // &lt;- RESTAPI를 사용한 예제라고 보면 된다.
        String url = &quot;https://api.externalwebsite.com/user&quot;; // 외부 API의 URL
        ResponseEntity&lt;String&gt; response = restTemplate.getForEntity(url, String.class);
        return response;
    }
}
</code></pre>
<hr>
<h2 id="web의-등장">Web의 등장</h2>
<blockquote>
</blockquote>
<p>유럽 인자 물리 연구소에서 세계의 여러 대학과 연구기관에서 일하는 물리학자 상호간 신속한 정보교환과 공동연구를 위해 개발된 Web</p>
<ul>
<li>웹페이지(Web Page)는 HTML, CSS, JS가 묶여져서 제공되는 페이지를 의미한다.</li>
</ul>
<h3 id="인트라넷intranet과-인터넷internet">인트라넷(Intranet)과 인터넷(Internet)</h3>
<blockquote>
</blockquote>
<p>인트라넷(Intranet)은 대학, 연구기관, 집과 같이 갇힌 공간에 국한된 네트워크를 의미하며, 
이러한 인트라넷들이 모여 <strong>게이트웨이(GateWay)</strong>를 통해 외부로 연결한 것을 <strong>인터넷(Internet)</strong>이라고 한다.</p>
<ul>
<li>외부로 연결하기 위해서는 게이트웨이를 사용해서 연결한다.</li>
<li>게이트웨이는 내부 네트워크(회선)과 외부 네트워크(회선) 연결 통로이다.</li>
</ul>
<hr>
<h2 id="dnsdomain-name-system">DNS(Domain Name System)</h2>
<blockquote>
</blockquote>
<p>서버가 사용하는 IP주소를 사람이 읽기 쉬운 형태인 별명(Alias)으로 변환하고 검색하는 과정</p>
<ul>
<li>모든 웹서버는 주소를 가지며, 이 주소의 명칭은 IP주소(네트워크 주소)이다. Ex) 126.10.1.12</li>
<li>하지만 이 IP주소는 사람이 읽기 어려운 형태이기 때문에 영어로 된 별명(Alias)인 도메인 네임을 사용한다. Ex) naver.com</li>
<li>웹 서버는 IP주소를 기반으로 통신하기 때문에 도메인 네임과 실제 IP주소가 어떻게 연결되어있는지가 중요하다.</li>
<li>이떄 사용하는 것이 <strong>&#39;DNS&#39;</strong>이다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>