<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>베이베이</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 01 Oct 2022 01:03:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>베이베이</title>
            <url>https://velog.velcdn.com/images/nyong_i/profile/a8f80d92-9bcf-4c00-a276-2635fe845340/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 베이베이. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/nyong_i" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HTTP와 HTTPS 차이점]]></title>
            <link>https://velog.io/@nyong_i/HTTP%EC%99%80-HTTPS-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@nyong_i/HTTP%EC%99%80-HTTPS-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Sat, 01 Oct 2022 01:03:42 GMT</pubDate>
            <description><![CDATA[<p>현재 거의 모든 웹 사이트들에서는 HTTPS를 지원하는데요. HTTP와 HTTPS의 차이점이 뭘까요?
저는 솔직히 HTTPS는 HTTP에서 보안이 추가되었다는것 밖에 몰랐습니다. 뭐 그게 대부분이긴 합니다..</p>
<p>자 그럼 HTTP의 약점을 먼저 알아보면서 시작하겠습니다.</p>
<h2 id="http의-약점">HTTP의 약점</h2>
<p>이 약점은 HTTP만이 아닌, 다른 암호화하지 않은 프로토콜에도 공통되는 문제입니다.</p>
<h2 id="1-평문이기-때문에-도청-가능">1. 평문이기 때문에 도청 가능</h2>
<p>HTTP를 사용한 리퀘스트나 리스폰스 통신 내용은 HTTP 자신을 암호화 하는 기능은 없기 때문에 통신 전체가 암호화 되지는 않습니다.</p>
<h3 id="도청이-가능하다">도청이 가능하다?</h3>
<p>도청이 가능하다는게 무슨소리냐?하면 TCP/IP는 구조상 전부 통신 경로를 엿볼 수 있습니다. 인터넷은 전 셰게와 이어져 있기 때문에, 통신 경로 상에 있는 라우터들이 자신 개인의 케이블이나 컴퓨터 등일 수가 없죠.</p>
<p>통신 내용을 엿본다는 것은 암호화도니 통신에서도 암호화되지 않은 통신도 같습니다. 음.. 암호화된 통신을 엿본다는 것은 메시지속의 의미는 볼 수 없지만 암호화된 상태의 메시지는 볼 수 있다는것이죠.</p>
<p>같은 세그먼트의 통신을 도청하는 것은 그렇게 어려운일이 아니라고 합니다. 네트워크 상을 흐르고 있는 패킷을 수집하는 것 만으로도 도청할 수 있습니다. 패킷을 수집하려면 패킷 캡쳐나 패킷 스니퍼라는 툴을 사용해야합니다.</p>
<h4 id="패킷-캡쳐">패킷 캡쳐</h4>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/72e2e244-7506-4b8f-9a68-f49fe1650c9e/image.png" alt=""></p>
<h4 id="패킷-스니퍼">패킷 스니퍼</h4>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/5d94138b-add2-4dd3-bda8-8cdbec9f1417/image.png" alt=""></p>
<h2 id="1-1-도청-피하기">1-1. 도청 피하기</h2>
<p>도청을 피할 수는 없습니다. 하지만 도청을 해도 내용을 볼 수 없게는 할 수 있습니다. 바로 암호화 방식입니다.</p>
<h3 id="통신-암호화">통신 암호화</h3>
<p>첫번째 방법은 암호화 방식입니다. HTTP에는 암호화 구조는 없지만 SSL이나 TLS라는 다른 프로토콜을 조합함으로써 HTTP의 통신 내용을 암호화할 수 있습니다.</p>
<p>SSL 등을 이용해 안전한 통신로를 만들어 놓고 그 통신로로 통신을 하는 방법입니다. 마치 터널과 비슷한 방식인 것 같습니다.</p>
<p>이렇게 SSL 등을 이용해 안전한 통신로를 확립하고 나서 그 통신로를 사용해 HTTP 통신을 합니다. HTTP 프로토콜에 SSL을 조합한 방식을 흔히들 알고있는 HTTPS(HTTP + SSL)이라고 부릅니다.</p>
<h3 id="콘텐츠-암호화">콘텐츠 암호화</h3>
<p>다른 한 가지는 통신하고 있는 콘텐츠의 내용 자체를 암호화해 버리는 방법입니다.</p>
<p>HTTP에 암호화 기능은 없기 때문에 HTTP를 사용해서 운반하는 내용을 암호화 하는 것입니다. 즉, HTTP 메시지에 포함되는 콘텐츠만 암호화 하는 것입니다.</p>
<p>이 경우, 클라이언트에서 HTTP 메시지를 암호화해서 출력하는 처리가 필요하게 됩니다.
물론, 콘텐츠의 암호화를 유효하게 하려면 클라이언트와 서버가 암호화된 콘텐츠를 복호화하는 기능을 가지고 있어야하는데, 평상시에 유저가 사용하는 브라우저나 웹 서버가 이런 이런 기능을 가지고 있기는 어렵습니다.</p>
<hr>
<h2 id="2-통신-상대를-모르기-때문에-위장-가능">2. 통신 상대를 모르기 때문에 위장 가능</h2>
<p>HTTP는 통신 상대를 모르고 통신한다는 것을 알고 계셨나요?</p>
<p>HTTP를 사용한 리퀘스트나 리스폰스에서는 통신 상대를 확인하지 않습니다. 리퀘스트를 보낸 서버가 정말로 URI에서 지정된 호스트인지 아닌지, 리스폰스를 반환한 클라이언트가 정말 리퀘스트를 출력한 클라이언트인지 아닌지 모른다는 것입니다.</p>
<h3 id="누구나-리퀘스트-할-수-있다">누구나 리퀘스트 할 수 있다.</h3>
<p>HTTP에 의한 통신에는 상대가 누구인지 확인하는 처리가 없기 떄문에 누구든지 르퀘스트를 보낼 수 있습니다. 또한 서버는 리퀘스트가 오면 상대가 누구든지 무언가의 리스폰스를 반환합니다.</p>
<p>이것은 매우 간단한 구조이기에 장점이라면 장점일 수는 있겠지만, 통신하는 상대를 모른다는 약점이 있습니다.
이 약점들도 여러가지인데 아래와 같습니다.</p>
<ul>
<li>위장한 웹 서버일 우려가 있다. - 리퀘스트를 보낸 곳의 웹 서버가 원래 의도한 리스폰스를 보내야 하는 웹 서버인지 아닌지를 확인할 수 없다</li>
<li>위장한 클라이언트일 우려가 있다. - 리스폰스를 반환한 곳의 클라이언트가 원래 의도한 리퀘스트를 보낸 클라이언트인지 아닌지를 확인할 수 없다.</li>
<li>통신하고 있는 상대가 접근이 허가된 상대인지 아닌지를 확인할 수 없다.</li>
<li>어디의 누가 리퀘스트를 했는지 알 수 없다.</li>
<li>D-Dos 공격을 방지할 수 없다. - 의미없는 리퀘스트라도 수신하게 된다.</li>
</ul>
<h3 id="ssl을-더-자세히">SSL을 더 자세히</h3>
<p>SSL을 쉽게 말하자면 상대를 확인하는 증명서라고 보면 편합니다. 사람으로 따지면 주민등록증..? 네.. 그런겁니다.</p>
<p>HTTP에서는 상대를 알 방법이 없기 때문에 SSL이라는 것으로 상대를 확인합니다.</p>
<p>증명서는 신뢰할 수 있는 제 3자 기관에 의해 발행되는 것이기 때문에 서버나 클라이언트가 실재하는 사실을 증명합니다. 또 그 증명서를 위조하는 것은 기술적으로 불가능한 것은 아닌데, 상당히 어렵습니다.</p>
<p>이렇게 상대의 서버나 클라이언트가 가진 증명서를 확인함으로써 통신 상대가 내가 통신하고자 하는 상대인지 아닌지를 판단할 수 있습니다.</p>
<hr>
<h2 id="3-완전성을-증명할-수-없기-때문에-변조-가능">3. 완전성을 증명할 수 없기 때문에 변조 가능</h2>
<blockquote>
<p>완전성이란? 정보의 정확성</p>
</blockquote>
<p>그것을 증명할 수 없다는 것은 정보가 정확한지 아닌지를 확인할 수 없음을 의미합니다.</p>
<h3 id="클라이언트에게서-받은-리퀘스트의-내용이-다를지도-모른다">클라이언트에게서 받은 리퀘스트의 내용이 다를지도 모른다.</h3>
<p>HTTP가 완전성을 증명할 수 없다는 것은 클라이언트가 서버에게 또는 서버가 클라이언트에게 전달한 리퀘스트나 리스폰스가 상대가 수신할 때까지의 사이에 변조되었을지 안되었을지 모른다는 이야기입니다.</p>
<p>변조되었다고 하더라도 이 사실을 알 수는 없죠.</p>
<p>이렇게 공격자가 리퀘스트나 리스폰스를 뺐어서 내용을 변조하는 공격을 Man-in-the-Middle 공격이라고 합니다.</p>
<hr>
<h2 id="따라서-https는">따라서 HTTPS는?</h2>
<p>HTTPS = (HTTP + 암호화 + 인증 + 완전성 보호)라고 볼 수 있겠네요.</p>
<p>자 오늘은 HTTP의 약점을 알아보고 SSL 방식으로 이 약점들을 보완하는 방식에 대해서 알아보았습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 서버와 HTTP가 통신하는 방법]]></title>
            <link>https://velog.io/@nyong_i/%EC%9B%B9-%EC%84%9C%EB%B2%84%EC%99%80-HTTP%EA%B0%80-%ED%86%B5%EC%8B%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@nyong_i/%EC%9B%B9-%EC%84%9C%EB%B2%84%EC%99%80-HTTP%EA%B0%80-%ED%86%B5%EC%8B%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 28 Sep 2022 14:47:42 GMT</pubDate>
            <description><![CDATA[<h2 id="서버-단-1대로-멀티-도메인을-가능하게-하는-가상-호스트">서버 &#39;단 1대&#39;로 멀티 도메인을 가능하게 하는 가상 호스트</h2>
<p>HTTP/1.1에서는 하나의 HTTP 서버에 여러 개의 웹 사이트를 실행할 수 있습니다. 예를 들면, 웹 호스팅을 제공하고 있는 사업자는 1대의 서버에 여러 고객의 웹 사이트를 넣을 수 있습니다.</p>
<p>고객마다 다른 도메인을 가지고, 다른 웹 사이트를 실행할 수 있는 것이죠. 이것은 가상 호스팅(Virtual Host)라는 기능을 사용하고 있기 때문입니다.</p>
<p>가상 호스팅을 사용하면 실제론 서버가 한 대이지만, 가상으로 서버가 여러대 있는 것 처럼 설정이 가능한것이죠.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/9b305ca1-22f2-4b9b-b194-42c87ab63319/image.png" alt=""></p>
<p>인터넷에서 도메인명은 DNS에 의해서 IP 주소로 변환되고 나서 액세스하게 됩니다. 이 과정은 아래와 같습니다. </p>
<p>Ex)</p>
<ul>
<li>클라이언트 : DNS에 <a href="https://www.google.com/">https://www.google.com/</a> 도메인 전달</li>
<li>DNS : 전달받은 도메인을 10.52.xx.xx IP 주소로 변환</li>
</ul>
<p>DNS가 도메인을 IP 주소로 변환하였기 떄문에, 결국은 리퀘스트가 IP 주소를 기준으로 액세스하게 됩니다.</p>
<p>근데 위에서 말한 가상 호스팅을 사용하는 상황이라면, 한 대의 서버 안에서 여러개의 호스트가 있기에 IP 주소는 같을텐데, 이런 경우 어느 쪽에 대한 액세스인지 알 수 없습니다.</p>
<p>따라서 HTTP 리퀘스트를 보내는 경우에는 호스트명과 도메인명을 완전하게 포함한 URI를 지정하거나, 반드시 Host 헤더 필드에서 지정해야 합니다.</p>
<hr>
<h2 id="통신을-중계하는-프록시-게이트웨이-터널">통신을 중계하는 프록시, 게이트웨이, 터널</h2>
<p>HTTP는 클라이언트와 서버 이외에 프록시, 게이트웨이, 터널과 같은 통신을 중계하는 프로그램과 서버를 연계하는 것도 가능합니다.</p>
<p>이러한 프로그램과 서버는 그 다음에 있는 다른 서버에 리퀘스트를 중계하고, 그 서버로부터 받은 리스폰스를 클라이언트에 반환하는 역할을 담당합니다.</p>
<hr>
<h3 id="프록시">프록시</h3>
<p>프록시는 클라이언트와 서버의 양쪽 역할을 하는 중간 다리의 역할입니다. 클라이언트가 서버에 리퀘스트를 보내면 프록시 서버를 걸쳐서 오리진 서버에 도착하죠.</p>
<blockquote>
<p>오리진 서버 : 리소스 본체를 가진 서버</p>
</blockquote>
<p>오리진 서버에 도착한 리퀘스트는 다시 프록시 서버를 걸쳐서 클라이언트에 돌아옵니다.</p>
<p>당연하게도 여러대의 프록시 서버를 경유하는 것도 가능합니다. </p>
<p>프록시 서버를 사용하면 좋은점이 있는데, 바로 <code>캐싱</code>입니다. 캐싱은 CS에서도 자주 등장하고, JPA 같은 기술들에서도 비슷하게 적용되는 사례들이 많습니다.</p>
<p>캐싱에 대해서는 아래서 더 자세히 설명하겠습니다.</p>
<p>프록시의 사용 방법은 여러 가지가 있는데, 크게 2가지로 나뉩니다.</p>
<h4 id="프록시의-사용-방법">프록시의 사용 방법</h4>
<ol>
<li>캐시를 하는지 안하는지 여부 판단</li>
<li>메시지를 변경 하는지 안하는지 여부 판단</li>
</ol>
<hr>
<h3 id="프록시---캐싱-프록시">프록시 - 캐싱 프록시</h3>
<p>프록시로 리스폰스를 중계하는 때에는 프록시 서버 상에 리소스 캐시를 보존해 두는 타입의 프록시입니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/05e20260-4712-4faa-94f0-add39ff6b939/image.png" alt=""></p>
<p>캐시를 간단하게 설명하자면 오리진 서버에 도달해서 한번 사용한 리소스를 프록시 서버에 보존해 두고 다음에 사용할 때 오리진 서버가 아닌 프록시 서버에서 캐시를 리스폰스로 돌려주는 방식입니다.</p>
<p>당연히 먼 곳까지 안가고 바로 앞에있는 곳에서 리스폰스를 받아오니 속도가 더 빨라집니다. </p>
<blockquote>
<p>Ex) 처음 웹 페이지를 요청했을 때와 한번 더 같은 페이지를 요청했을 때 시간차이가 나는 것</p>
</blockquote>
<p>하지만 캐시 서버에 캐시가 있는 경우라도 같은 리소스의 리퀘스트에 대해서 항상 캐시를 돌려 준다고는 말할 수 없는데요.</p>
<p>그 이유는 캐시에 유효기간이 있기때문입니다. 이것은 캐시되어 있는 리소스의 유효성과 관계가 있습니다.</p>
<blockquote>
<p>etc) 클라이언트도 인터넷 임시 파일이라고 불리는 캐시가 있습니다.</p>
</blockquote>
<hr>
<h3 id="프록시---투명-프록시">프록시 - 투명 프록시</h3>
<p>프록시로 리퀘스트와 리스폰스를 중계할 때 메시지 변경을 하지 않는 타입의 프록시를 투명 프록시라고 합니다.</p>
<p>반대로 메시지에 변경을 하는 타입을 비투과 프록시라고 합니다.</p>
<hr>
<h3 id="게이트웨이">게이트웨이</h3>
<p>게이트웨이의 동작은 프록시와 거의 같은데요, 게이트웨이는 다른 서버를 중계하는 서버로 클라이언트로부터 받은 리퀘스트를 리소스를 보유한 오리진 서버인 것 마냥 받습니다.</p>
<p>이게 무슨 말이냐면 게이트웨이는 &quot;서버인 척 하는 중간다리&quot;라고 생각하시면 될 것 같습니다.</p>
<p>또한 게이트웨이는 클라이언트 사이를 암호화하는 방식으로 통신의 안전성을 높입니다.</p>
<p>예를 들면 게이트웨이는 데이터베이스에 접속해서 데이터를 얻는데 사용하거나, 보안이 중요한 결제 시스템등에 사용됩니다.</p>
<hr>
<h3 id="터널">터널</h3>
<p>터널은 눈에 보이지 않는 투명한 개념입니다. 터널은 여규에 따라서 다른 서버와의 통신 경로를 확립합니다. </p>
<p>이 떄 클라이언트는 암호화 통신을 통해 서버와 안전하게 통신하기 위해 사용합니다.</p>
<p>터널 자체는 리퀘스트를 해석하려고 하지 않으며 리퀘스트를 받은 그대로 다음 서버에 넘겨줍니다. 터널은 통신중인 양쪽 끝의 접속이 끊어질 때에 종료됩니다.</p>
<blockquote>
<p>한마디로 터널은 -&gt; 안전한 통신로를 확립해주는 개념 (터널을 통해서 리퀘스트가 이동하면 안전하다고 생각하면 됩니다.)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠키(Cookie)]]></title>
            <link>https://velog.io/@nyong_i/%EC%BF%A0%ED%82%A4Cookie</link>
            <guid>https://velog.io/@nyong_i/%EC%BF%A0%ED%82%A4Cookie</guid>
            <pubDate>Sat, 24 Sep 2022 17:18:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>HTTP 1.1 기준으로 작성한 글입니다.</p>
</blockquote>
<hr>
<h2 id="쿠키는-왜-만들어졌을까">쿠키는 왜 만들어졌을까?</h2>
<h3 id="http는-상태를-유지하지-않는-stateless-프로토콜">HTTP는 상태를 유지하지 않는 stateless 프로토콜</h3>
<p>HTTP는 <strong>상태를 계속 유지하지 않는</strong> <code>스테이트리스</code>(stateless) 프로토콜입니다. 
HTTP 프로토콜 독자적으로, request와 response를 교환하는 동안에 상태를 관리하지 않는것인데요.</p>
<p>이는 이전에 보냈던 리퀘스트나 되돌려준 리스폰스에 대해서 전혀 기억하지 못한다는 뜻입니다.</p>
<p>이는 많은 데이터를 <strong>빠르고 정확하게 처리</strong>하는 <code>범위성</code>(scalability)을 확보하기 위해서 이렇게 설계되어 있는 것입니다.</p>
<p>하지만 장점만 보고 이런 stateless한 프로토콜을 사용하기에는 단점이 너무나도 컸는데, 예를 들면 <code>로그인</code>입니다.</p>
<p>로그인이 필요한 서비스의 경우 당연하게도 다른페이지로 이동하더라도 로그인 상태를 유지해야할 의무가 있습니다.</p>
<p>하지만 stateless 프로토콜인 HTTP/1.1은 상태를 유지하지 않기 때문에 범위성은 가져오되 상태 관리를 할 수 있도록 해주는 <code>쿠키</code>(Cookie)라는 기술을 도입했습니다.</p>
<hr>
<h2 id="쿠키cookie란">쿠키(Cookie)란?</h2>
<p>쿠키는 리퀘스트와 리스폰스에 <code>쿠키 정보</code>를 추가해서 클라이언트의 상태를 파악하기 위한 시스템입니다.</p>
<p>쿠키는 서버에서 리스폰스로 보내진 <code>Set-Cookie</code>라는 <code>헤더 필드</code>에 의해 쿠키를 클래이언트에 보존하게 됩니다. 다음 번에 클라이언트가 서버로 리퀘스트를 보낼 때 자동으로 쿠키 값을 넣어서 송신하죠.</p>
<p>그렇게되면 서버가 쿠키를 통해 이전의 리퀘스트와 리스폰스의 상태를 확인할 수 있게됩니다.</p>
<p>순서로보면 아래와 같습니다.</p>
<ol>
<li>클라이언트가 서버에게 리퀘스트 송신</li>
<li>리스폰스에 Set-Cookie로 쿠키를 붙여서 송신</li>
<li>클라이언트가 서버에게 쿠키를 붙여서 다시 서버에게 리퀘스트 송신</li>
<li>서버가 쿠키를 통해서 req, res 이전 상태 확인</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크의 기본은 TCP/IP]]></title>
            <link>https://velog.io/@nyong_i/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EA%B8%B0%EB%B3%B8%EC%9D%80-TCPIP</link>
            <guid>https://velog.io/@nyong_i/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EA%B8%B0%EB%B3%B8%EC%9D%80-TCPIP</guid>
            <pubDate>Thu, 22 Sep 2022 04:20:59 GMT</pubDate>
            <description><![CDATA[<p><code>HTTP</code>를 이해하기 위해서는 <code>TCP/IP</code> 프로토콜에 대해 어느정도 알고 있어야 할 필요가 있습니다.</p>
<p>인터넷을 포함하여 일반적으로 사용하고 있는 네트워크는 <code>TCP/IP</code>라는 프로토콜에서 움직이고 있습니다.</p>
<hr>
<h3 id="tcpip는-프로토콜의-집합">TCP/IP는 프로토콜의 집합</h3>
<p>컴퓨터와 네트워크 기기가 상호간에 통신하기 위해서는 <strong>서로 같은 방법으로 통신</strong>해야합니다. </p>
<p>따라서 서로 다른 하드웨어와 운영체제등을 가지고 통신하기 위해서는 규칙이 필요한데, 이러한 규칙을 <code>프로토콜</code>이라 부릅니다.</p>
<p>프로토콜에는 여러 가지가 있는데, 케이블 규격, IP 주소 지정 방법, 목적지에 도달하는 순서 등등..</p>
<p>이렇게 인터넷과 관련된 프로토콜들을 모은것을 <code>TCP/IP</code>라고 부르며 대표적으로 여러분들이 잘 아는 <code>HTTP</code> 프로토콜도 포함되어 있습니다.</p>
<hr>
<h3 id="tcpip는-계층으로-이루어져있다">TCP/IP는 계층으로 이루어져있다?</h3>
<p>TCP/IP에서 중요한 개념 중에 하나가 계층(layer)입니다. TCP/IP의 계층은 아래와 같이 나눠져있습니다.</p>
<ul>
<li>애플리케이션 계층</li>
<li>트랜스포트 계층</li>
<li>네트워크 계층</li>
<li>링크 계층</li>
</ul>
<h4 id="tcpip-계층화의-장점">TCP/IP 계층화의 장점</h4>
<ol>
<li>여러개의 프로토콜들로 이루어져있기 때문에 어디선가 사양이 변경되었을 때 해당 계층만 바꾸면 된다.</li>
<li>각 계층은 계층이 연결되어 있는 부분만 결정되어 있어, 각층의 내부를 자유롭게 설계할 수 있다.</li>
<li>설계를 편하게 할 수 있다.</li>
</ol>
<hr>
<h4 id="애플리케이션-계층">애플리케이션 계층</h4>
<p><code>애플리케이션 계층</code>은 유<strong>저에게 제공되는 애플리케이션에서 사용하는 통신의 움직임을 결정</strong>합니다.</p>
<p>TCP/IP에는 여러 가지의 <strong>공통 애플리케이션</strong>이 준비되어 있습니다. Ex) FTP, DNS, HTTP ..</p>
<hr>
<h4 id="트랜스포트-계층">트랜스포트 계층</h4>
<p><code>트랜스포트 계층</code>은 <strong>애플리케이션 계층에 네트워크로 접속되어 있는 2대의 컴퓨터 사이의 데이터 흐름을 제공</strong>합니다. </p>
<p>트랜스포트 계층에서는 서로다른 성질을 가진 <code>TCP</code>와 <code>UDP</code> 두가지 프로토콜이 있습니다.</p>
<hr>
<h4 id="네트워크-계층">네트워크 계층</h4>
<p>네트워크 계층은 네트워크 상에서 <code>패킷</code>의 이동을 다룹니다.</p>
<blockquote>
<p>패킷이란? 전송하는 데이터의 최소 단위</p>
</blockquote>
<p>이 계층에서는 <strong>어떤 경로를 걸쳐서 상대의 컴퓨터까지 패킷을 전송시킬지</strong> 정하기도 합니다.</p>
<p>인터넷의 경우에 상대 컴퓨터에 도달하는 동안에 <strong>여러 대의 컴퓨터와 네트워크 기기를</strong> 거쳐서 상대방에게 전달됩니다.</p>
<p>따라서 여러 컴퓨터와 네트워크 기기중 어떤 길로 갈지 <code>하나의 길</code>을 결정하는 것이 네트워크 계층의 역할이 됩니다.</p>
<hr>
<h4 id="링크-계층">링크 계층</h4>
<p><strong>네트워크에 접속하는 하드웨어적인 면을 다룹니다.</strong> 운영체제가 하드웨어를 제어하기 때문에 디바이스 드라이버와 네트워크 인터페이스 카드를 포합합니다.</p>
<p><strong>물리적으로 보이는부분</strong>(케이블 등등..)도 포함됩니다. 쉽게말하면 하<strong>드웨어적인 부분들은 모두 TCP/IP 계층중에서 링크 계층의 역할</strong>입니다.</p>
<hr>
<h3 id="tcpip-통신은-u자형">TCP/IP 통신은 U자형</h3>
<p>TCP/IP 통신을 할 때는 계층을 순서대로 거쳐 상대와 통신을 합니다. 송신(보내는 쪽)은 애플리케이션 계층에서부터 내려가고, 수신(뱓는 쪽)은 애플리케이션 계층으로 올라갑니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/f0a43d48-ecc4-4423-b2ff-d2cd6e7fb516/image.png" alt=""></p>
<p>예를 들어 설명해보겠습니다.</p>
<ol>
<li><p>애플리케이션 계층 : 어느 웹 페이지를 보고싶다고 리퀘스트를 지시합니다.</p>
</li>
<li><p>트랜스포트 계층 : 애플리케이션 계층에서 받은 데이터를 작게 여러조각으로 조각내어 번호표를 부여하여 네트워크 계층에 전송합니다.</p>
</li>
<li><p>네트워크 계층 : 수신지 MAC 주소를 추가해서 링크 계층에 전달합니다. <em><del>(MAC 주소는 아래서 설명합니다.)</del></em></p>
</li>
</ol>
<p>이렇게 송신측의 준비는 끝났습니다. 수신은 이와 반대 순서로 진행된다고 보면 됩니다. 사진으로 보면 아래와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/9d813bf1-333c-45e8-9c94-398b8294bc2b/image.png" alt=""></p>
<ul>
<li><p><strong>송신</strong> : 각 계층을 거칠 때는 반드시 헤더로 불려지는 해당 계층마다 해당 계층에 필요한 정보를 <strong>추가</strong>합니다.</p>
</li>
<li><p><strong>수신</strong> : 각 계층을 거칠 때마다 반드시 해당 계층마다 사용한 헤더를 <strong>삭제</strong>합니다.</p>
</li>
</ul>
<hr>
<h3 id="http와-깊은-관계인-ip--tcp--dns">HTTP와 깊은 관계인 IP / TCP / DNS</h3>
<p><code>IP</code>, <code>TCP</code>, <code>DNS</code>는 HTTP와 관계가 정말 깊습니다. 따라서 세 개의 프로토콜에 대해서 설명을 해보겠습니다.</p>
<h4 id="ip---배민-라이더">IP - 배민 라이더</h4>
<p>IP는 계층으로 말하자면 네트워크 층에 해당됩니다. <code>IP</code>는 TCP/IP라는 명칭의 일부가 될 정도로 중요한 프로토콜입니다.</p>
<blockquote>
<p>IP와 IP 주소는 다른 개념입니다. IP는 프로토콜의 명칭입니다.</p>
</blockquote>
<p>IP의 역할은 <strong>개개의 패킷을 상대에게 전달하는 것</strong>입니다. 상대방에게 전달하기까지 여러 가지 요소가 필요한데, 그 중에서도 <code>IP 주소</code>와 <code>MAC 주소</code>가 중요합니다.</p>
<ul>
<li>IP 주소 : 각 노드에 부여된 주소</li>
<li>MAC 주소 : 각 네트워크 카드에 할당된 고유의 주소</li>
</ul>
<p>IP 주소와 MAC 주소는 연관되어있으며 IP 주소는 변경가능하지만, MAC 주소는 변경 불가능합니다.</p>
<hr>
<h4 id="arp와-mac">ARP와 MAC</h4>
<p>IP 통신은 MAC 주소에 의존해서 통신을 합니다. </p>
<p>인터넷에서 통신 상대가 같은 랜선 내에 있을 경우는 많지 않기 때문에 여러 대의 컴퓨터와 네트워크 기기를 통해서 상대방에게 도착합니다.</p>
<p>그렇게 여러 컴퓨터와 네트워크 기기를 지나는 동안 다음 목적지의 MAC 주소를 통해 목적지를 찾아갈 수 있는것입니다.</p>
<blockquote>
<p>ARP : 주소 문제를 해결하기 위한 프로토콜 중 하나이며 수신지의 IP 주소를 바탕으로 MAC 주소를 조사할 수 있다.</p>
</blockquote>
<p>Ex)
송신 : 52.54.xx.xx 주소에 패킷을 보내고 싶음
ARP : 52.54.xx.xx 주소에 도달하기 위해 거치는 중간 라우터들의 MAC 주소를 조사해서 패킷을 목적지에 도달시킴</p>
<hr>
<h4 id="신뢰성을-담당하는-tcp">신뢰성을 담당하는 TCP</h4>
<p>TCP는 계층으로 말하자면 트랜스포트 계층에 해당하는데, 신뢰성 있는 바이트 스트림 서비스를 제공합니다.</p>
<blockquote>
<p>바이트 스트림 서비스? 용량이 큰 데이터를 보내기 쉽게 TCP 세그먼트라고 불리는 단위 패킷으로 작게 분해하여 관리하는 서비스</p>
</blockquote>
<blockquote>
<p>신뢰성 있는? 상대방에게 <strong>보내는</strong> 서비스</p>
</blockquote>
<p>따라서 TCP는 대용량의 데이터를 보내기 쉽도록 작게 분해하여 상대에게 보내고, 정확하게 도착했는지 확인하는 역할을 담당하고 있습니다.</p>
<p>신뢰성이 있도록 상대에게 확실하게 데이터를 보내기 위해서 TCP는 쓰리웨이 핸드셰이킹이라는 방법을 사용합니다. <a href="https://velog.io/@nyong_i/TCP%EC%99%80-UDP%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-">이 글</a>에 정리해 놓았습니다.</p>
<hr>
<h4 id="이름-해결을-담당하는-dns">이름 해결을 담당하는 DNS</h4>
<p>DNS는 HTTP와 같이 응용 계층 시스템에서 도메인 이름과 IP 주소 이름 확인을 제공합니다. 컴퓨터는 IP 주소와는 별도로 호스트 이름과 도메인 이름을 붙일 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/a81baf48-3f12-473b-90bf-414e1ab557f4/image.jpeg" alt=""></p>
<p>사용자들은 숫자를 나열하는 IP 주소보다 영문과 숫자 등으로 표기한 이름이 더 친숙합니다. 하지만 컴퓨터들은 숫자를 나열하는 방식이 더 친숙한데 이러한 문제를 해결하기 위해 DNS가 있습니다.</p>
<p>DNS는 도메인명에서 IP 주소를 조사하거나 반대로 IP 주소로부터 도메인명을 조사하는 서비스를 제공합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 2.0은 1.1이랑 뭐가 다를까?]]></title>
            <link>https://velog.io/@nyong_i/HTTP-2.0%EC%9D%80-1.1%EC%9D%B4%EB%9E%91-%EB%AD%90%EA%B0%80-%EB%8B%A4%EB%A5%BC%EA%B9%8C</link>
            <guid>https://velog.io/@nyong_i/HTTP-2.0%EC%9D%80-1.1%EC%9D%B4%EB%9E%91-%EB%AD%90%EA%B0%80-%EB%8B%A4%EB%A5%BC%EA%B9%8C</guid>
            <pubDate>Tue, 13 Sep 2022 03:32:19 GMT</pubDate>
            <description><![CDATA[<h2 id="속도의-차이">속도의 차이</h2>
<p>당연스럽게도 HTTP의 버전업이 이루어 지면서 <code>속도</code>가 빨라졌습니다.</p>
<p>이는 HPACK 압축으로 헤더를 줄이고 응답의 우선순위를 정해 응답을 보내는 방식을 사용하는 것이 HTTP 1.1보다 더 빠른 응답속도를 기록할수 있는 이유입니다.</p>
<blockquote>
<p>헤더를 줄이고..<del>~ 우선순위를 정하고</del> 하는 것은 </p>
</blockquote>
<h2 id="헤더를-줄이고-우선순위를-정하고-뭐라는거야">헤더를 줄이고..<del>~ 우선순위를 정하고</del> 뭐라는거야</h2>
<p>결국 HTTP 1.1의 단점을 보완했다는 이야기입니다. HTTP 1.1의 단점은 아래와 같습니다.</p>
<ul>
<li><p><code>Connection</code> <strong>한 개당 하나</strong>의 요청을 처리하도록 설계됨</p>
<ul>
<li>동시에 리소스를 주고 받을 수 없음</li>
<li><code>request</code>와<code>response</code>가 순차적으로 이루어짐</li>
<li>HTTP 문서 내에 많은 리소스(image, css, script)를 처리하려면 요청할 리소스의 개수에 비례하여 대기시간이 길어짐</li>
</ul>
</li>
<li><p><code>HOL(Head Of Line) Blocking</code>이 발생할 수 있다.
HOL 블로킹이란 네트워크에서 같은 큐에있는 <strong>첫번째 패킷이 지연되므로써 성능이 저하되는 현상</strong>이다.</p>
</li>
<li><p><code>RTT(Round Trip Time)</code> 증가
첫번째 단점에서 말한  특성때문에 매번 요청 별로 Connection을 만들게 되고 <code>TCP</code>상에서 동작하는 HTTP의 특성상 <code>3-way handshake</code>가 반복적으로 일어나서 불필요한 RTT 증가와 네트워크 지연을 초래하여 성능을 지연시킨다.</p>
</li>
<li><p>무거운 Header 구조
매 요청마다 중복된 헤더 값을 전송하게 되며, 서버 도메인에 관련된 쿠키 정보도 헤더에 함께 포함되어 전송된다. 이러한 반복적인 헤더 전송, 쿠키 정보로 인해 헤더 크기가 증가한다.</p>
</li>
</ul>
<hr>
<h2 id="그래서-이런걸-보완한-http-20">그래서 이런걸 보완한 HTTP 2.0</h2>
<p><code>HTTP2.0</code>은 <code>HTTP1.1</code>을 완전하게 재작성한 것이 아니라 프로토콜의 성능에 초첨을 맞춰 <code>수정한 버전</code>이라고 생각하면 됩니다.</p>
<p>특히 지연시간이나 네트워크, 서버 리소스 사용량 등과 같은 성능 위주로 개선되었습니다.</p>
<h3 id="주요-특징">주요 특징</h3>
<ul>
<li><code>Multiplexed Streams</code> : connection 한 개로 동시에 여러 개의 메시지를 주고 받을 수 있도록 개선</li>
<li><code>Stream Prioritization</code> : 리소스 간의 의존관계에 따른 우선순위를 설정하여 리소스 로드 문제를 해결함</li>
<li><code>Header Compression</code> : 헤더 정보를 HPACK 방식으로 압축한다.</li>
</ul>
<hr>
<h3 id="references">references</h3>
<ul>
<li><a href="https://bakjuna.tistory.com/129">https://bakjuna.tistory.com/129</a></li>
<li><a href="https://velog.io/@wiostz98kr/HTTP1.1%EA%B3%BC-HTTP2.0%EC%9D%98-%EC%B0%A8%EC%9D%B4-e2v4x4t1">https://velog.io/@wiostz98kr/HTTP1.1%EA%B3%BC-HTTP2.0%EC%9D%98-%EC%B0%A8%EC%9D%B4-e2v4x4t1</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링에서 Scheduler 사용하기]]></title>
            <link>https://velog.io/@nyong_i/%EC%8A%A4%ED%94%84%EB%A7%81%EC%97%90%EC%84%9C-Scheduler-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@nyong_i/%EC%8A%A4%ED%94%84%EB%A7%81%EC%97%90%EC%84%9C-Scheduler-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 08 Sep 2022 01:50:14 GMT</pubDate>
            <description><![CDATA[<p>간혹 개발하다가 일정 주기별로 메서드를 실행시켜주고 싶을 때가 있다.
나 또한 Open API를 일정 주기마다 받아와야하는 상황이 있었다. Spring에서 지원해주는 <code>Scheduler</code>를 사용해서 일정 주기마다 메서드를 실행시켜보자.</p>
<hr>
<h2 id="dependency-추가하기">Dependency 추가하기</h2>
<p><code>Scheduler</code>는 기본적으로 <code>org.springframework.scheduling</code> 패키지에 있으므로 <code>gradle</code> 기준 다음과 같이 의존성을 추가하면 된다.</p>
<pre><code class="language-java">dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
}</code></pre>
<p><code>maven</code>의 경우 다음과 같다.</p>
<pre><code class="language-maven">&lt;dependency&gt; 
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<hr>
<h2 id="스케줄링-활성화">스케줄링 활성화</h2>
<p>스케줄링 기능을 활성화하기 위해서 다음과 같이 <code>configuration</code> 클래스 혹은 지금처럼 애플리케이션 <code>메인 클래스</code>에 <code>@EnableScheduling</code>를 추가한다.</p>
<pre><code class="language-java">@EnableScheduling
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
</code></pre>
<hr>
<h2 id="컴포넌트-등록">컴포넌트 등록</h2>
<pre><code class="language-java">@Component
public class ScheduledTasks {

}</code></pre>
<p>기본적으로 <code>@Component</code>를 사용해서 컴포넌트로 등록해도 되지만, <code>@Component</code>를 내장하고 있는 <code>@Service</code>와 같은 어노테이션들로도 등록이 가능하다.</p>
<hr>
<h2 id="크론-문법으로-반복-주기-지정하기">크론 문법으로 반복 주기 지정하기</h2>
<p>스케쥴러를 이용하기 위해서는 여러가지 방법들이 있지만, 난 그중에서 보기쉽고 직관적인 <code>@Scheduled</code> + <code>cron</code> 문법을 사용해서 지정해 보겠다.</p>
<blockquote>
<p>스케줄러가 적용된 메서드의 반환타입은 <code>void</code>여야 합니다.</p>
</blockquote>
<pre><code class="language-java">@Scheduled(cron = &quot;0 0 0 * * *&quot;)
public void ScheduledTasksMethod() {
    log.info(&quot;{}에 실행되었습니다.&quot;, formatter.format(LocalDateTime.now()));
}</code></pre>
<p>참고로 위에서 사용한 <code>0 0 0 * * *</code> 은 <code>매일 자정</code>이라는 뜻입니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/8d44b804-331c-4c7c-8a31-f9e43ac0163e/image.png" alt=""><a href="https://madplay.github.io/post/a-guide-to-cron-expression">출처</a></p>
<hr>
<p>이렇게 스케줄러를 사용해서 일정 주기마다 실행되는 메서드를 만들어 보았다. 크론 문법은 아직 미숙하기 때문에 더 파봐야겠다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 상태 코드]]></title>
            <link>https://velog.io/@nyong_i/HTTP-%EC%83%81%ED%83%9C-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@nyong_i/HTTP-%EC%83%81%ED%83%9C-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Sun, 28 Aug 2022 13:21:10 GMT</pubDate>
            <description><![CDATA[<h2 id="response-status---1">Response Status - 1**</h2>
<h3 id="100-continue">100 Continue</h3>
<p>이 임시적인 응답은 지금까지의 <strong>상태가 괜찮으며</strong> 클라이언트가 계속해서 요청을 하거나 이미 요청을 완료한 경우에는 <strong>무시해도 되는 것</strong>을 알려준다.</p>
<h3 id="101-switching-protocol">101 Switching Protocol</h3>
<p>이 코드는 클라이언트가 보낸 <code>Upgrade(en-US)</code> 요청 헤더에 대한 응답에 들어가며 <strong>서버에서 프로토콜을 변경할 것</strong>임을 알려준다.</p>
<h3 id="102-processing-webdav-en-us">102 Processing (WebDAV (en-US))</h3>
<p>이 코드는 서버가 요청을 수신하였으며 이를 처리하고 있지만, <strong>아직 제대로 된 응답을 알려줄 수 없음</strong>을 알려준다.</p>
<h3 id="103-early-hints">103 Early Hints</h3>
<p>이 상태 코드는 주로 Link(en-US) 헤더와 함께 사용되어 서버가 응답을 준비하는 동안 사용자 <code>에이전트가(user agent)</code> <code>사전 로딩(preloading)</code>을 시작할 수 있도록 한다.</p>
<hr>
<h2 id="response-status---2">Response Status - 2**</h2>
<h3 id="200-ok">200 OK</h3>
<p>요청이 성공적으로 됨을 알림. </p>
<p>성공의 의미는 HTTP 메소드에 따라 달라진다.</p>
<ul>
<li>GET: 리소스를 불러와서 메시지 바디에 전송</li>
<li>HEAD: 개체 해더가 메시지 바디에 있음</li>
<li>PUT 또는 POST: 수행 결과에 대한 리소스가 메시지 바디에 전송됨</li>
<li>TRACE: 메시지 바디는 서버에서 수신한 요청 메시지를 포함하고 있음</li>
</ul>
<h3 id="201-created">201 Created</h3>
<p><strong>요청이 성공적</strong>이었으며 그 결과로 새로운 리소스가 생성됨. 이 응답은 일반적으로 POST 요청 또는 일부 PUT 요청 이후에 따라온다.</p>
<h3 id="202-accepted">202 Accepted</h3>
<p>요청을 수신하였지만 그에 응하여 행동할 수 없다. 이 응답은 요청 처리에 대한 결과를 이후에 HTTP로 비동기 응답을 보내는 것에 대해서 명확하게 명시하지 않는다. 이것은 다른 프로세스에서 처리 또는 서버가 요청을 다루고 있거나 배치 프로세스를 하고 있는 경우를 위해 만들어졌다.</p>
<h3 id="203-non-authoritative-information-en-us">203 Non-Authoritative Information (en-US)</h3>
<p>이 응답 코드는 돌려받은 메타 정보 세트가 오리진 서버의 것과 일치하지 않지만 로컬이나 서드 파티 복사본에서 모아졌음을 의미합니다. 이러한 조건에서는 이 응답이 아니라 200 OK 응답을 반드시 우선시된다.</p>
<h3 id="204-no-content">204 No Content</h3>
<p>요청에 대해서 보내줄 수 있는 콘텐츠가 없지만, 헤더는 의미있을 수 있다. 사용자-에이전트는 리소스가 캐시된 헤더를 새로운 것으로 업데이트 할 수 있다.</p>
<hr>
<h2 id="response-status---3">Response Status - 3**</h2>
<h3 id="300-multiple-choice-en-us">300 Multiple Choice (en-US)</h3>
<p><strong>요청에 대해서 하나 이상의 응답이 가능</strong>하다. 사용자 에이전트 또는 사용자는 그중에 하나를 반드시 선택해야 한다. 응답 중 하나를 선택하는 방법에 대한 표준화 된 방법은 존재하지 않는다.</p>
<h3 id="301-moved-permanently">301 Moved Permanently</h3>
<p><strong>요청한 리소스의 URI가 변경되었음을 의미</strong>한다. 새로운 URI가 응답에서 아마도 주어질 수 있다.</p>
<h3 id="302-found">302 Found</h3>
<p><strong>요청한 리소스의 URI가 일시적으로 변경되었음을 의미</strong>한다. 새롭게 변경된 URI는 나중에 만들어질 수 있다. 그러므로, 클라이언트는 향후의 요청도 반드시 동일한 URI로 해야한다.</p>
<h3 id="303-see-other-en-us">303 See Other (en-US)</h3>
<p>클라이언트가 요청한 리소스를 다른 URI에서 GET 요청을 통해 얻어야 할 때, 서버가 클라이언트로 직접 보내는 응답이다.</p>
<h3 id="304-not-modified">304 Not Modified</h3>
<p>이것은 <strong>캐시를 목적으로 사용</strong>된다. 이것은 클라이언트에게 응답이 수정되지 않았음을 알려주며, 그러므로 클라이언트는 계속해서 응답의 캐시된 버전을 사용할 수 있다.</p>
<hr>
<h2 id="response-status---4">Response Status - 4**</h2>
<h3 id="400-bad-request">400 Bad Request</h3>
<p><strong>잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음</strong>을 의미한다.</p>
<h3 id="401-unauthorized">401 Unauthorized</h3>
<p>비록 HTTP 표준에서는 &quot;미승인(unauthorized)&quot;를 명확히 하고 있지만, 의미상 이 응답은 <strong>&quot;비인증(unauthenticated)&quot;을 의미</strong>한다. 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 한다.</p>
<h3 id="403-forbidden">403 Forbidden</h3>
<p><strong>클라이언트는 콘텐츠에 접근할 권한를 가지고 있지 않다.</strong></p>
<h3 id="404-not-found">404 Not Found</h3>
<p><strong>서버는 요청받은 리소스를 찾을 수 없다.</strong> 브라우저에서는 알려지지 않은 URL을 의미한다. 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있다. 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모른다.</p>
<h3 id="405-method-not-allowed">405 Method Not Allowed</h3>
<p><strong>요청한 메소드는 서버에서 알고 있지만, 제거되었고 사용할 수 없다.</strong> 예를 들어, 어떤 API에서 리소스를 삭제하는 것을 금지할 수 있다.</p>
<h3 id="407-proxy-authentication-required-en-us">407 Proxy Authentication Required (en-US)</h3>
<p>이것은 401과 비슷하지만 <strong>프록시에 의해 완료된 인증이 필요</strong>하다.</p>
<h3 id="408-request-timeout">408 Request Timeout</h3>
<p><strong>이 응답은 요청을 한지 시간이 오래된 연결에 일부 서버가 전송</strong>하며, 어떨 때에는 이전에 클라이언트로부터 어떠한 요청이 없었다고 하더라도 보내지기도 한다. 이것은 서버가 사용되지 않는 연결을 끊고 싶어한다는 것을 의미한다. </p>
<h3 id="409-conflict">409 Conflict</h3>
<p><strong>요청이 현재 서버의 상태와 충돌</strong>될 때 보낸다.</p>
<hr>
<h2 id="response-status---5">Response Status - 5**</h2>
<h3 id="500-internal-server-error">500 Internal Server Error</h3>
<p><strong>서버가 처리 방법을 모르는 상황이 발생</strong>함. 서버는 아직 처리 방법을 알 수 없다.</p>
<h3 id="501-not-implemented">501 Not Implemented</h3>
<p>요청 방법은 서버에서 지원되지 않으므로 처리할 수 없다. 서버가 지원해야 하는 유일한 방법은 GET와 HEAD이다. 이 코드는 반환하면 안된다.</p>
<h3 id="502-bad-gateway">502 Bad Gateway</h3>
<p>이 오류 응답은 서버가 요청을 처리하는 데 필요한 응답을 얻기 위해 <strong>게이트웨이로 작업하는 동안 잘못된 응답을 수신했음을 의미</strong>한다.</p>
<h3 id="504-gateway-timeout">504 Gateway Timeout</h3>
<p>이 오류 응답은 서버가 게이트웨이 역할을 하고 있으며 적시에 응답을 받을 수 없을 때 주어진다.</p>
<h3 id="505-http-version-not-supported">505 HTTP Version Not Supported</h3>
<p>요청에 사용된 HTTP 버전은 서버에서 지원되지 않는다.</p>
<h3 id="506-variant-also-negotiates-en-us">506 Variant Also Negotiates (en-US)</h3>
<p>서버에 내부 구성 오류가 있다. 즉, 요청을 위한 투명한 컨텐츠 협상이 순환 참조로 이어진다.</p>
<h3 id="507-insufficient-storage-en-us">507 Insufficient Storage (en-US)</h3>
<p>서버에 내부 구성 오류가 있다. 즉, 선택한 가변 리소스는 투명한 콘텐츠 협상에 참여하도록 구성되므로 협상 프로세스의 적절한 종료 지점이 아니다.</p>
<h3 id="508-loop-detected-en-us-webdav-en-us">508 Loop Detected (en-US) (WebDAV (en-US))</h3>
<p>서버가 요청을 처리하는 동안 <strong>무한 루프를 감지</strong>함.</p>
<h3 id="511-network-authentication-required-en-us">511 Network Authentication Required (en-US)</h3>
<p><strong>클라이언트가 네트워크 액세스를 얻기 위해 인증을 받아야 할 필요가 있음</strong>을 나타낸다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP Method]]></title>
            <link>https://velog.io/@nyong_i/HTTP-Method</link>
            <guid>https://velog.io/@nyong_i/HTTP-Method</guid>
            <pubDate>Sun, 28 Aug 2022 11:50:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="http-method는-크게-4가지로-나누어져있다">HTTP Method는 크게 4가지로 나누어져있다.</h4>
</blockquote>
<ul>
<li>Create: 데이터 생성 (POST)</li>
<li>Read: 데이터 조회 (GET)</li>
<li>Update: 데이터 수정 (PUT)</li>
<li>Delete: 데이터 삭제 (DELETE)</li>
</ul>
<p>보통 <code>GET</code>과 <code>POST</code>의 차이는 명확히 구분할 수 있지만, <code>POST</code>와 <code>PUT</code>를 구분하려면 멱등성을 알아야 한다.</p>
<h3 id="💡-멱등성idempotence이란">💡 멱등성(Idempotence)이란?</h3>
<p><code>멱등성</code>이란 여러번 수행해도 결과가 같음을 의미한다.</p>
<p>HTTP 메소드를 예를 들자면, GET, PUT, DELETE는 같은 경로로 여러 번 호출해도 결과가 같다.</p>
<p>그러나 POST는 매 호출마다 새로운 데이터가 추가된다. 
따라서, POST 연산은 결과가 Idempotent하지 않지만, PUT은 반복 수행해도 그 결과가 Idempotent 하다. </p>
<hr>
<h3 id="⚪️-get">⚪️ GET</h3>
<p><code>GET</code> 메소드는 주로 데이터를 <code>읽거나</code>(Read) <code>검색</code>(Retrieve)할 때에 사용되는 메소드이다. </p>
<p>만약에 <code>GET</code>요청이 성공적으로 이루어진다면 <code>200</code>(OK) HTTP 응답 코드를 리턴한다. 에러가 발생하면 상태코드 <code>404</code> (Not found) 또는 <code>400</code> (Bad request)를 반환한다.</p>
<p>HTTP 명세에 의하면 GET 요청은 오로지 데이터를 읽을 때만 사용된다고 한다. 따라서 <strong>데이터를 변경할 때</strong>는 사용하면 안된다.</p>
<blockquote>
<p>Ex)</p>
</blockquote>
<pre><code class="language-javascript">GET /post/3</code></pre>
<p>데이터를 조회하는 것이기 때문에 요청시에 <code>Body</code> 값과 <code>Content-Type</code> 이 비워져있다. 조회할 데이터에 대한 정보는 URL을 통해서 파라미터를 받고 있는 모습을 볼 수 있다.</p>
<p>데이터 조회에 성공한다면 Body 값에 데이터 값을 저장하여 성공 응답을 보낸다.</p>
<p><code>GET</code>은 <code>캐싱</code>이 가능하여 같은 데이터를 한번 더 조회할 경우에 조회 속도가 빨라진다.</p>
<hr>
<h3 id="🟢-post">🟢 POST</h3>
<p><code>POST</code> 메소드는 주로 새로운 리소스를 <code>생성</code>(create)할 때 사용된다. 성공적으로 작업을 완료하면 <code>201</code>(Created) HTTP 응답을 반환한다.</p>
<p>POST 메소드는 아래와 같은 특징을 가지고있다.</p>
<ul>
<li>같은 POST 요청을 반복해서 했을 때 <strong>항상 같은 결과물이 나오는 것을 보장하지 않는다</strong></li>
<li>두 개의 같은 POST 요청을 보내면 같은 정보를 담은 두 개의 다른 resource를 반환할 가능성이 높다.</li>
</ul>
<blockquote>
<p>Ex)</p>
</blockquote>
<pre><code class="language-javascript">POST /user
body: 
{
  email: &quot;example@gmail.com&quot;, 
  password: &quot;test123!@&quot;
}
Content-Type: &quot;application/json&quot;</code></pre>
<p>데이터를 생성하는 것이기 때문에 요청시에 Body 값과 Content-Type 값을 작성해야한다.</p>
<p>URL을 통해서 데이터를 받지 않고, <code>Body</code> 값을 통해서 받는다.</p>
<p>데이터 조회에 성공한다면 Body 값에 저장한 데이터 값을 저장하여 성공 응답을 보낸다.</p>
<hr>
<h3 id="🔵-put">🔵 PUT</h3>
<p><code>PUT</code>은 리소스를 <code>생성/수정</code>하기 위해 서버로 데이터를 보내는 데 사용된다.</p>
<p>PUT은 아래와 같은 특징을 가지고있다.</p>
<ul>
<li>PUT 요청은 idempotent하다.</li>
<li>동일한 PUT 요청을 여러 번 호출하면 <strong>항상 동일한 결과가 생성</strong>된다.</li>
</ul>
<blockquote>
<p>Ex)</p>
</blockquote>
<pre><code class="language-javascript">PUT /user/1
body: 
{
    email: &quot;updateEmail@gmail.com&quot;, 
    password: &quot;updatePassword123!@&quot;
}
Content-Type: &quot;application/json&quot;</code></pre>
<p>데이터를 수정하는 것이기 때문에 요청시에 Body 값과 Content-Type 값을 작성해야한다.</p>
<p>URL을 통해서 어떠한 데이터를 수정할지 파라미터 받는다. 그리고 수정할 데이터 값을 Body 값을 통해서 받는다.</p>
<hr>
<h3 id="🔴-delete">🔴 DELETE</h3>
<p><code>DELETE</code> 메서드는 지정된 리소스를 삭제한다.</p>
<blockquote>
<p>Ex)</p>
</blockquote>
<pre><code class="language-javascript">DELETE /user/1</code></pre>
<p>데이터를 삭제하는 것이기 때문에 요청시에 Body 값과 Content-Type 값이 비워져있다.</p>
<hr>
<h3 id="references">references</h3>
<ul>
<li><a href="https://devuna.tistory.com/77">https://devuna.tistory.com/77</a></li>
<li><a href="https://velog.io/@yh20studio/CS-Http-Method-%EB%9E%80-GET-POST-PUT-DELETE#1-get">https://velog.io/@yh20studio/CS-Http-Method-%EB%9E%80-GET-POST-PUT-DELETE#1-get</a></li>
<li><a href="https://memostack.tistory.com/180#2.1.%20GET%20Method">https://memostack.tistory.com/180#2.1.%20GET%20Method</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis]]></title>
            <link>https://velog.io/@nyong_i/Redis</link>
            <guid>https://velog.io/@nyong_i/Redis</guid>
            <pubDate>Mon, 22 Aug 2022 14:42:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/nyong_i/post/8842fe58-83bd-4305-9ba9-75c712ba489f/image.png" alt="">
약 3년전 쿠팡에서 10시간이 넘도록 <code>&quot;대&quot;장애</code>가 일어났었는데 장애의 원인은 <code>레디스</code>였다고 한다.</p>
<p>이렇게 우리의 곁에서도 심심치않게 등장하며, 특히 jwt 또는 채팅에 자주 사용하곤 하는데 이녀석이 뭘까?</p>
<hr>
<h2 id="레디스란">레디스란?</h2>
<p>Redis란 <strong>RE</strong>mote <strong>DI</strong>ctionary <strong>S</strong>erver의 약자이다.
<code>Remote</code>(외부) <code>Dictionary</code>(key, value) <code>Server</code>(서버) 따라서 Redis는 외부에 있는 딕셔너리를 사용하는 서버라고 볼 수 있다.</p>
<hr>
<h2 id="what-is-redis-🤔">What is Redis 🤔</h2>
<ul>
<li><p>인 메모리 데이터 구조 : <code>List</code>, <code>Set</code>, <code>Hash</code>, <code>Map</code> 등의 유형들을 인 메모리 안에 저장 할 수 있다. <code>Redis</code> 중 큰 특징이라 생각한다.</p>
</li>
<li><p>검색 시간으로 인한 지연을 방지하고 <code>CPU 명령</code>을 적게 사용하는 좀 더 간단한 알고리즘으로 데이터에 액세스할 수 있다</p>
</li>
<li><p>개발과 운영을 좀 더 쉽게 할 수 있으며 <code>Pub/Sub</code> 사용하여 메시징 시스템에 유용하며 불필요한 데이터를 채우지 않게하는 데 유용하다.</p>
</li>
<li><p><code>캐시</code>를 사용하여 <strong>속도가 빠르고</strong> <strong>정보가 사리지지 않습니다.</strong></p>
</li>
</ul>
<hr>
<h2 id="레디스의-필요성">레디스의 필요성</h2>
<ul>
<li><p><code>캐시</code>를 생성하여 <code>액세스 시간</code>을 <strong>줄이고</strong> <code>처리량</code>을 <strong>늘려서</strong> DB에 부담을 덜어줌</p>
</li>
<li><p>이벤트 속도를 측정하고 제어할 수 있음</p>
</li>
<li><p><strong>List</strong>를 사용하면 <code>영구 대기열</code>을 만들 수 있다. <strong>자동 작업 및 차단</strong> 기능을 제공하므로 신뢰할 수 있는 메시지 브로커, 순환 목록이 필요한 다양한 애플리케이션에 사용한다.</p>
</li>
<li><p>패턴 매칭과 더불어 <code>PUB/SUB</code>이 지원이 된다고 한다. <code>고성능 채팅방</code>, <code>실시간 코멘트 스트림 및 서버 상호 통신</code>을 지원한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jwt에 대한 의견과 고찰]]></title>
            <link>https://velog.io/@nyong_i/Jwt%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EA%B2%AC%EA%B3%BC-%EA%B3%A0%EC%B0%B0</link>
            <guid>https://velog.io/@nyong_i/Jwt%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EA%B2%AC%EA%B3%BC-%EA%B3%A0%EC%B0%B0</guid>
            <pubDate>Mon, 01 Aug 2022 17:06:03 GMT</pubDate>
            <description><![CDATA[<p>요즘 개발을 하면서 <code>Authentication</code>에 관련된 것들은 거의 <code>Spring Security</code> + <code>Jwt</code>로 개발한다.</p>
<p>처음에 <code>Jwt</code>를 접했을 때 <code>Jwt</code>가 무엇이고 <code>헤더</code>, <code>페이로드</code>, <code>액세스 토큰</code>, <code>리프레시 토큰</code> 등 뭐가 뭐고 얘가 이런애고 쟤는 저거 하는 애고 이론적으로는 그닥 어렵진 않았다.</p>
<p>하지만 <code>백문이 불여일타</code>라고 했다. (( 내가하고 싶은 말은 반대되긴 하지만..
원래 백번 말하는것보다 한번 치는게 더 이해된다고 하던데, 한번 쳐보려하니 머릿속으로는 정리가 됬던 이론들이 제각각 놀기 시작했다.</p>
<p>물론 다른사람 블로그도 보고 깃허브도 보며 염탐을 오지게 해보긴 했지만, 사바사(사람 바이 사람)이여서 그런지 느낌은 비슷하긴해도 복붙으로 해결할 수 없는 부분들이 많았다.</p>
<p><code>스프링 부트</code>에서 <code>Jwt</code>를 어떤식으로 사용하고 리프레시 토큰을 사용한 방식으로 어떻게 액세스 토큰을 재발급하는가 등등 여러가지 <code>고난</code>과 <code>역경</code>을 겪었다;</p>
<p>뭐 하여튼, 지금부터 내가 Jwt를 처음 배우고 스프링 부트에 적용하면서 겪었던 <code>난관?</code>을 소개해보겠다. 뭐 솔직히 나만 이해못하고 다른사람들은 거의 이해했을 것이다.</p>
<blockquote>
<p>이 글은 Jwt를 소개하는 글이 아니라 내가 개발하면서 Jwt를 어떻게 스프링에 적용시켜야 하는지 했던 고찰이다.</p>
</blockquote>
<h2 id="스프링-부트에서-jwt-방식으로-인증을-구현하는-이유">스프링 부트에서 Jwt 방식으로 인증을 구현하는 이유</h2>
<p>우리가 알고있는 대표적인 인증 처리 방식은 <code>3가지</code>가 있다.(쿠키, 세션, Jwt)
<code>Jwt</code>는 개인적으로 셋중에서는 제일 까다롭게 느껴졌다.</p>
<p>그래서 이런생각을 해봤다. 
셋 다 인증되는건 똑같은데 왜 <code>Jwt</code>를 써야하냐?</p>
<p>이 궁금증이 해결되지 않으면 나에게 있어서 <code>Jwt</code>란 다른 2가지 인증 방식보다 <strong>개발 생산성만 떨어지는 인증 방식</strong>이라고 인식했을 것이다.</p>
<p>그래서 내가 생각하는 <code>Jwt</code>를 사용해야 하는 이유를 몇가지 살펴보자.</p>
<p>토큰 방식은 사용자가 로그인을 하면 서버에서 발행해주는 토큰을 가지고
브라우저의 저장소에 토큰을 <code>유지</code>시키는 방법이다.
여기서 말하는 토큰이 우리가 말하는 <code>Jwt</code>이다.</p>
<h3 id="확장성">확장성</h3>
<p>서버에 저장하지 않아서 서버에 <code>확장성</code>이 있다.
위에서 말했듯 로그인을 했을 때 해당 서버에만 요청을 보내는 것이 아닌
요청이 들어왔을 때 해당 <strong>토큰이 유효한지만 체크</strong>하면 되기 때문에
어떤 서버로 요청을 보내도 상관이 없다는 뜻이다.</p>
<h3 id="정보-교환">정보 교환</h3>
<p><code>Jwt</code>는 당사자 간에 정보를 안전하게 전송하는 좋은 방법이다. <code>헤더</code>와 <code>페이로드</code>를 사용하여 서명을 계산하므로 내용이 조작되지 않았는지 확인할 수도 있다.</p>
<h3 id="트래픽에-대한-부담">트래픽에 대한 부담</h3>
<p>서버에 직접적으로 관여하는 부분이 크지 않기 때문에 세션보다 트래픽에 대한 부담이 적다.</p>
<hr>
<h2 id="1-토큰의-정보는-어디에">1. 토큰의 정보는 어디에?</h2>
<p>Jwt에 대해서 뭣도 모르고 &quot;쉬워 보이네ㅋㅋ 일단 해보자&quot;라는 생각에 흥분하며 인텔리제이를 실행시켰던 때가 엇그제 같다. 시작하자마자 아무것도 모르고 개발을 한다는건 망상이었다.</p>
<p>Jwt라는 방식으로도 인증할 수 있구나 라는 생각으로 개발을 시작했었던 것 같은데 당연하겠지만 최소한의 Jwt에 관련된 이론적인 지식들은 알아야 개발이 가능하다.</p>
<p>다시 본론으로 돌아와서 액세스 토큰과 리프레시 토큰의 차이점은 <code>유효시간</code>에 있으며, 나머지 정보들은 같다. </p>
<p>이를 바탕으로 로그인 요청을 보내면 <code>유효시간</code>만 다른 토큰 2개가 반환되어야 하는데.. </p>
<p>결론부터 말하자면 방법은 쉬웠지만 매번 바뀌는 토큰의 <code>시간</code>을 전역 변수로 선언해야 하는가에 대해 오래 고민했다. </p>
<p><strong>시간만 따로 관리해주는 클래스</strong>를 만들어서 사용하면 더 쉽게 접근할 수 있었다.</p>
<p>우선 아시겠다시피 Jwt는 액세스 토큰과 리프레시 토큰이 있으며, </p>
<blockquote>
<p>액세스 토큰이 만료되면, 리프레시 토큰을 통해서 액세스 토큰을 재발급 받는다.</p>
</blockquote>
<p>라는 개념이 잡혀있어야 한다.</p>
<p>이것이 성립하려면 리프레시 토큰이 액세스 토큰보다 유효 시간이 길어야 하는것이 성립되어야 하기 때문에 리프레시 토큰의 유효 시간을 길게 잡아준다.</p>
<p>액세스 토큰의 유효시간을 짧게 잡는 이유는 Jwt를 발급한 후에는 만료되기 전까지는 토큰을 무효화 할 수 없기 때문에 보안이 취약하기 때문이다.</p>
<p>보통 액세스 토큰의 유효 시간은 약 1시간 ~ 2시간 이며, 리프레시 토큰은 짧으면 2주 ~ 한 달까지 잡기도 한다고 한다. (( 뭐 이것도 사바사이긴 함..</p>
<p>이제 약간의 코드를 섞어가면서 살펴보자.</p>
<hr>
<h3 id="gradle">gradle</h3>
<pre><code class="language-java">dependencies {
    implementation group: &#39;io.jsonwebtoken&#39;, name: &#39;jjwt-api&#39;, version: &#39;0.11.2&#39;
    runtimeOnly group: &#39;io.jsonwebtoken&#39;, name: &#39;jjwt-impl&#39;, version: &#39;0.11.2&#39;
    runtimeOnly group: &#39;io.jsonwebtoken&#39;, name: &#39;jjwt-jackson&#39;, version: &#39;0.11.2&#39;
}</code></pre>
<p>Jwt를 사용하기 위해서는 당연히 Jwt 라이브러리를 땡겨와야 한다.</p>
<hr>
<h3 id="applicationyml">application.yml</h3>
<pre><code class="language-yml">spring:
    security:
        jwt:
            secret: c2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQtc2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQK</code></pre>
<p><code>HS512 알고리즘</code>을 사용할 것이기 때문에 <code>512bit</code>, 즉 <code>64byte</code> 이상의 <code>secret key</code>를 사용해야 한다. </p>
<p>쉽게 말하면 저자리에는 그냥 아무거나 길게 적어놓으면 된다는 말임..</p>
<hr>
<h3 id="액세스-토큰">액세스 토큰</h3>
<p>뭐 간단하게 토큰의 생성과 유효성 검증등을 담당할 클래스를 만들어주자.</p>
<pre><code class="language-java">@Component
public class JwtTokenProvider {

}</code></pre>
<p>여기저기서 의존성 주입을 받으며 사용당해야 하기 때문에 <code>@Component</code> 어노테이션을 사용한다.</p>
<pre><code class="language-java">@Value(&quot;${spring.security.jwt.secret}&quot;)
private String secretKey;</code></pre>
<p><code>@Value</code>로 아까 <code>yml</code>에 작성해 놓았던 시크릿 키를 가져오자. 따로 설정을 하지 않았다면 기본 경로는 <code>resources</code> 아래에 있는 <code>application.yml</code>로 지정될 것이다.</p>
<pre><code class="language-java">@PostConstruct
protected void init() {
     secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}</code></pre>
<p><code>@PostConstruct</code>는 의존성 주입이 이루어진 후 초기화를 수행하는 메서드이다.</p>
<p>따라서 이 컴포넌트는 의존성 주입이 이루어진 후 시크릿 키가 Base64로 <code>인코딩</code> 될 것이다.</p>
<pre><code class="language-java">public String createToken(페이로드에 넣고 싶은 정보(매개변수가 몇개이든 상관없음), 토큰 유효 시간) {
        Claims claims = Jwts.claims();
        claims.put(&quot;email&quot;, email);
        Date now = new Date();

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(new Date(now.getTime() + time))
                .signWith(getSigningKey(secretKey), SignatureAlgorithm.HS256)
                .compact();
    }</code></pre>
<p><code>claim</code>은 <code>payload</code> 부분에 들어갈 정보 조각들이다.</p>
<blockquote>
<p><code>claim</code>은 <code>Registered claim</code>, <code>Public claim</code>, <code>Private claim</code>으로 나눠진다.</p>
</blockquote>
<p>나같은 경우에는 <code>페이로드</code> 부분에 사용자의 <code>이메일</code> 정보를 넣어줬다.</p>
<h4 id="jwtbuilder에서는"><code>Jwt.builder</code>에서는</h4>
<p><code>헤더의 타입(typ)</code>, <code>발급 시간(iat)</code>, <code>만료 시간(exp)</code>, <code>비공개 클레임</code>을 설정할 수 있다.</p>
<p><code>발급 시간(iat)</code>과 <code>만료 시간(exp)</code>은 Date 타입만 추가가 가능하다.</p>
<p><code>비공개 클레임</code>은 <code>key-value</code> 쌍으로 이루어져 있으며, <code>헤싱 알고리즘</code>과 <code>시크릿 키</code>를 설정할 수 있다.</p>
<pre><code class="language-java">public String createAccessToken(페이로드에 넣고싶은 정보) {
    return createToken(페이로드에 넣고싶은 정보, 토큰 유효 시간);
}

public String createRefreshToken(String email) {
    return createToken(페이로드에 넣고싶은 정보, 토큰 유효 시간);
}</code></pre>
<p>이렇게 액세스 토큰과 리프레시 토큰을 생성하는 코드를 작성해보았다.</p>
<p>위에서 언급했듯이 이 코드에 <strong>토큰 유효 시간만 따로 관리하는 클래스</strong>를 만들어서 적용시켜도 되고, <code>전역 변수</code>로 선언해도 안될건 없다.</p>
<hr>
<h2 id="2-액세스-토큰의-재발급에-대해서">2. 액세스 토큰의 재발급에 대해서</h2>
<p>내가 액세스 토큰을 리프레시 토큰을 이용해서 재발급 받는 구조를 이해 못해서 그냥 액세스 토큰을 리프레시 토큰만큼 길게 잡아서 액세스 토큰을 리프레시 토큰마냥 사용하려 했던적이 있다.</p>
<p>(난 처음에 엑세스 토큰이 만료되면 리프레시 토큰으로 자동 변경되는줄 알았다ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ)</p>
<p>하지만 리프레시 토큰은 리프레시 토큰이고 액세스 토큰은 액세스 토큰이 아닌가. 리프레시 토큰을 이용해서 액세스 토큰을 재발급한다는게 무슨 말인지 알아보자.</p>
<p>우선 액세스 토큰을 1시간으로, 리프레시 토큰은 2주일로 설정해놓았다고 가정하자. 따라서 액세스 토큰은 유효시간이 짧기 때문에 비교적으로 재발급 받는 빈도가 많을 수 밖에 없다.</p>
<p>따라서 리프레시 토큰을 사용하는 이유는 1시간마다 사용자 재로그인이라는 미친 번거로움을 없애고, 토큰 무효화 기능 관련 부하 문제를 막을 수 있다.</p>
<p>(리프레시 토큰마저 만료된다면, 다시 로그인해야함)</p>
<p>액세스 토큰과 달리 리프레시 토큰은 발급시 데이터베이스에 저장하고, 액세스 토큰을 재발급 받을 시 리프레시 토큰을 request header에 담은 후 요청을 보내고, 데이터베이스에 있는 리프레시 토큰과 같으면 새로운 액세스 토큰을 재발급 해주는 형태이다.</p>
<p>백문이 불여일견이라고했다. (나는 이 말을 참 좋아하는 것 같다..) 사진으로 보자.</p>
<h3 id="로그인-후-액세스-토큰과-리프레시-토큰-발급">로그인 후 액세스 토큰과 리프레시 토큰 발급</h3>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/061b1248-fb4e-4bc3-a26a-f365bcdbf389/image.png" alt=""></p>
<h3 id="액세스-토큰-재발급">액세스 토큰 재발급</h3>
<p>기존의 액세스 토큰이 만료됐다고 가정하고 재발급 받아야 하는 상황
<img src="https://velog.velcdn.com/images/nyong_i/post/33604467-08e0-4eac-9fc3-0d61e23a4ac6/image.png" alt="">
처음에 로그인을 하고 발급 받았던 리프레시 토큰을 헤더에 넣어주고 요청을 보내면!</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/67db6888-47b3-49b5-a863-bebc97e850bb/image.png" alt=""></p>
<p>만료되지 않은 새 액세스 토큰으로 반환된다.
리프레시 토큰이 null인 이유는 같은 로그인을 했을때와 같은 responseDto로 반환했는데, 액세스 토큰만 재발급 해서 그렇다. 신경쓰지 않아도 됨..</p>
<p>여기에 관련된 내용은 <a href="https://www.bezkoder.com/spring-boot-refresh-token-jwt/">이 포스트</a>가 정리가 잘되어있다.</p>
<hr>
<h2 id="3-매-요청마다-토큰이-만료되었는지-어떻게-확인하는가">3. 매 요청마다 토큰이 만료되었는지 어떻게 확인하는가</h2>
<p>액세스 토큰을 재발급 받으려면 우선 토큰이 만료되었는지 안되었는지 먼저 알아야한다. </p>
<p>액세스 토큰이 만료되었는지 안되었는지 판단하는 기능이 어려운 것이 아니었다.
이를 어떻게 매 요청마다 확인할지가 고민이었다.</p>
<p>이것도 결론부터 말하면 <code>Filter</code>에 있었는데, <code>SecurityConfig</code>라는 시큐리티 설정 클래스에서 스프링 시큐리티가 지원하는 필터체인 전에 실행하도록 하는 <code>.addFilterBefore()</code>메서드를 사용했다.</p>
<pre><code class="language-java">    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, customUserDetailService, jwtValidateService),
                        UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JwtExceptionFilter(), JwtAuthenticationFilter.class);
    }</code></pre>
<p>이렇게 토큰이 유효한지 판단하는 클래스와, Jwt의 정보를 필요로 하는 클래스들을 먼저 필터링 하게 만들었다.</p>
<hr>
<h2 id="끝내며">끝내며</h2>
<p>이번에 작성한 글은 Jwt로 어떻게 로그인을 해야하고 사용하는지 세세하게 다루기 보다는 내가 Jwt를 스프링 부트에 사용하면서 어떤걸 고민했고, 어떻게 사용해서 문제들을 풀어나갔는지에 대한 관점들이 주를 이루었다.</p>
<p>이번에 Jwt로 개발해보면서 Jwt 뿐만 아니라 스프링 전반적인 이해도 또한 많이 길러진 것 같다.</p>
<p>처음에 언급했다시피 다른사람들의 글과 코드를 많이 보았다.
내가 하던 것보다 더 좋은 방법들도 많아 보였다. </p>
<p>비록 이해하고 사용하자는 내 고질병 덕분에 개발부터 글까지 쓰는 시간이 오래 걸리긴 했지만, 여전히 이해하고 사용해야 한다는 생각은 달라지지 않은 것 같다.</p>
<p>계속 열심히 개발합시다.. 여러분 화이팅!</p>
<hr>
<h3 id="references">References</h3>
<ul>
<li><a href="https://www.bezkoder.com/spring-boot-refresh-token-jwt/">Spring Boot Refresh Token with JWT example</a></li>
<li><a href="https://velog.io/@dyparkkk/spring-sequrity-jwt-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0access-token-refresh-token">spring security jwt 사용하기</a></li>
<li><a href="https://kukekyakya.tistory.com/entry/Spring-boot-access-token-refresh-token-%EB%B0%9C%EA%B8%89%EB%B0%9B%EA%B8%B0jwt">Spring Boot jwt 사용하기 access token, refresh token 발급</a></li>
<li><a href="https://aejeong.com/entry/Spring-boot-JWT-RefreshToken-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0">Spring boot + JWT + RefreshToken 구현하기</a></li>
<li><a href="https://ayoteralab.tistory.com/entry/Spring-Boot-32-JWT-%EC%9D%B8%EC%A6%9D-%EA%B0%B1%EC%8B%A0%EC%B2%98%EB%A6%AC-1">JWT 인증 갱신처리</a></li>
<li><a href="https://llshl.tistory.com/32">[Spring] JWT Refresh Token을 사용한 로그인과 고찰
</a></li>
<li><a href="https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT">[WEB] 📚 Access Token &amp; Refresh Token 원리 (feat. JWT)
</a></li>
<li><a href="https://github.dev/Bamdoliro/Gati-server">Bamdoliro</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Batch - Job]]></title>
            <link>https://velog.io/@nyong_i/Spring-Batch-Job</link>
            <guid>https://velog.io/@nyong_i/Spring-Batch-Job</guid>
            <pubDate>Sun, 24 Jul 2022 10:20:52 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-batch">Spring Batch</h2>
<p><code>Spring Batch</code>는 <code>로깅/추적</code>, <code>트랜잭션 관리</code>, <code>작업 처리 통계</code>, <code>작업 재시작</code>, <code>건너뛰기</code>, <code>리소스 관리</code> 등 <strong>대용량 레코드 처리</strong>에 필수적인 기능을 제공한다. </p>
<p>또한 <strong>최적화 및 파티셔닝</strong> 기술을 통해 <code>대용량</code> 및 <code>고성능</code> 배치 작업을 가능하게 하는 고급 기술 서비스 및 기능도 제공한다.</p>
<p><code>Spring Batch</code>에서 배치가 실패하여 작업 재시작을 하게 된다면 처음부터가 아닌 <code>실패한 지점</code>부터 실행을 하게 된다.</p>
<p>또한 중복 실행을 막기 위해 성공한 이력이 있는 Batch는 동일한 Parameters로 실행 시 <code>Exception</code>이 발생하게 된다.</p>
<hr>
<h2 id="batch-중에서도-spring-batch로">Batch 중에서도 Spring Batch로..</h2>
<p><code>Spring Batch</code>의 특징은 다음과 같다.</p>
<ul>
<li>가볍고 포괄적인 배치 프레임워크</li>
<li><code>Spring Batch</code>는 <code>로깅/추적</code>, <code>트랜잭션 관리</code>, <code>작업 처리 통계</code>, <code>작업 재시작</code>, <code>건너뛰기</code>, <code>리소스 관리</code> 등 <strong>대용량 레코드 처리</strong>에 필수적인 기능을 제공</li>
<li>최적화, 파티셔닝 기술로 대용량 고성능 배치 작업 가능</li>
<li>확정성이 매우 뛰어남</li>
</ul>
<hr>
<h2 id="job">Job</h2>
<p>1개의 작업에 대한 명세서이다. 어디까지가 1개의 작업인지 애매할 수 있지만 그 기준은 상황별로 재량껏 판단할 수 있다.</p>
<h3 id="job의-특징">Job의 특징</h3>
<ul>
<li>1개의 <code>Job</code>은 여러개의 <code>Step</code>을 포함할 수 있다.</li>
<li><code>Job name</code>을 통해 <code>Job</code>을 구분할 수 있다.</li>
<li><code>Job name</code>으로 <code>Job</code>을 실행시킬 수 있다.</li>
<li><code>Job Builder Factory</code>로 쉽게 <code>Job</code>을 만들 수 있다.</li>
</ul>
<blockquote>
<p>Ex ) Job : 게임을 한다.
Step 1. 컴퓨터를 켠다.
Step 2. 게임에 로그인 한다.
Step 3. 게임 시작을 누른다.</p>
</blockquote>
<hr>
<h3 id="jobinstance">JobInstance</h3>
<p><code>JobInstance</code>는 <code>Job</code>의 실행의 단위를 나타냅니다. <code>Job</code>을 실행시키게 되면 하나의 <code>JobInstance</code>가 생성된다. </p>
<p>예를들어 1월 1일 실행, 1월 2일 실행을 하게 되면 각각의 <code>JobInstance</code>가 생성되며 1월 1일 실행한 <code>JobInstance</code>가 실패하여 다시 실행을 시키더라도 이 <code>JobInstance</code>는 1월 1일에 대한 데이터만 처리하게 된다.</p>
<hr>
<h3 id="jobexecution">JobExecution</h3>
<p><code>JobExecution</code>은 <code>JobInstance</code>에 대한 실행 시도에 대한 객체이다. </p>
<p>1월 1일에 실행한 <code>JobInstacne</code>가 실패하여 재실행을 하여도 <strong>동일한</strong> <code>JobInstance</code>를 실행시키지만 이 2번에 실행에 대한 <code>JobExecution</code>은 <strong>개별</strong>로 생기게 된다.</p>
<p><code>JobExecution</code>는 이러한 <code>JobInstance</code> 실행에 대한 <code>상태</code>, <code>시작시간</code>, <code>종료시간</code>, <code>생성시간</code> 등의 정보를 담고 있다.</p>
<hr>
<h3 id="jobexecutioncontext">JobExecutionContext</h3>
<p>1개의 Job 내에서 공유하는 공간(Contex)이다.</p>
<p>1개의 Job에 여러개의 Step이 있다면 그 Step들은 해당 공간을 공유할 수 있다.</p>
<hr>
<h3 id="jobparameters">JobParameters</h3>
<p>Job이 시작될 때 필요한 시작 조건을 JobParameters에 넣는다.</p>
<p>동일한 Job에 JobParameters까지 동일하면 같은 JobInstence이다.</p>
<blockquote>
<p>Ex ) 편의점 리스트를 가져와서 저장하는 Job</p>
</blockquote>
<ul>
<li>밤 10시에는 C편의점 리스트의 정보 갱신</li>
<li>밤 11시에는 G편의점 리스트의 정보 갱신</li>
</ul>
<blockquote>
<p>조건 )</p>
</blockquote>
<ul>
<li>밤 10시에는 &#39;JobParameter = C편의점&#39;으로 구동되어야 함</li>
<li>밤 11시에는 &#39;JobParameter = G편의점&#39;으로 구동되어야 함</li>
</ul>
<hr>
<h3 id="jobbuilderfactory를-가지고-올-수-없다면">JobBuilderFactory를 가지고 올 수 없다면?</h3>
<p>Spring Batch에서는 EnableBatchProcessing 어노테이션을 달면 아래 bean들을 사용할 수 있도록 미리 구현되어있다.</p>
<p>Spring Batch 프로젝트를 만들게 된다면 필수적으로 EnableBatchProcessing를 추가해 주어야 한다.</p>
<pre><code class="language-java">@EnableBatchProcessing를
@SpringBootApplication
public class Application {

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

}</code></pre>
<h4 id="enablebatchprocessing으로-사용할-수-있게된-bean들">EnableBatchProcessing으로 사용할 수 있게된 bean들</h4>
<ul>
<li>JobRepository</li>
<li>JobLuncher</li>
<li>JobRegister</li>
<li>JobExplorer</li>
<li>PlatformTransactionManager</li>
<li>JobBuilderFactory</li>
<li>StepBuilderFactory</li>
</ul>
<hr>
<h3 id="reference">reference</h3>
<p><a href="https://khj93.tistory.com/entry/Spring-Batch%EB%9E%80-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">Spring Batch란? 이해하고 사용하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[회원가입 + 로그인 구현하기 [Jwt + Spring Security]]]></title>
            <link>https://velog.io/@nyong_i/Jwt%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@nyong_i/Jwt%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 11 Jul 2022 03:18:35 GMT</pubDate>
            <description><![CDATA[<h2 id="초기-설정">초기 설정</h2>
<h3 id="buildgradle">build.gradle</h3>
<pre><code class="language-java">plugins {
    id &#39;org.springframework.boot&#39; version &#39;2.7.1&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.0.11.RELEASE&#39;
    id &#39;java&#39;
}

group = &#39;the.plural&#39;
version = &#39;0.0.1-SNAPSHOT&#39;
sourceCompatibility = &#39;11&#39;

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-oauth2-client&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-security&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-thymeleaf&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-validation&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-websocket&#39;
    implementation &#39;org.thymeleaf.extras:thymeleaf-extras-springsecurity5&#39;
    implementation &#39;io.jsonwebtoken:jjwt:0.9.1&#39;
    compileOnly &#39;org.projectlombok:lombok&#39;
    runtimeOnly &#39;mysql:mysql-connector-java&#39;
    annotationProcessor &#39;org.projectlombok:lombok&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
    testImplementation &#39;org.springframework.security:spring-security-test&#39;
}

tasks.named(&#39;test&#39;) {
    useJUnitPlatform()
}
</code></pre>
<hr>
<p>데이터베이스는 <code>mysql</code>을 사용하고 <code>Jwt</code> 토큰 기반 방식으로 로그인할 것이기 때문에 <code>jwt</code> 관련 설정을 추가해준다.</p>
<h3 id="applicationyml">application.yml</h3>
<pre><code class="language-yml">spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/데이터베이스 이름?serverTimezone=Asia/Seoul&amp;characterEncoding=UTF-8
    username: 유저네임
    password: 비밀번호

  security:
    jwt:
      header: Authorization
      secret: c2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQtc2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQK
      token-validity-in-seconds: 86400

  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    generate-ddl: true
    hibernate:
      ddl-auto: update
    show_sql: true
    format_sql: true

#    default_batch_fetch_size: 1000

logging.level:
  org.hibernate.SQL: debug
  org.hibernate.type: trace
  # parameter Binding</code></pre>
<hr>
<h2 id="회원가입">회원가입</h2>
<h3 id="basetimeentity">BaseTimeEntity</h3>
<pre><code class="language-java">@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;
}
</code></pre>
<ul>
<li>createDate : 엔티티가 생성된 시간</li>
<li>modifiedDate : 엔티티가 수정된 시간</li>
</ul>
<p>엔티티의 생성시간과 수정시간 컬럼을 만들기 위해서 <code>BaseTimeEntity</code> 클래스를 생성한다.</p>
<hr>
<h3 id="application">Application</h3>
<pre><code class="language-java">@EnableJpaAuditing // 이 부분
@SpringBootApplication
public class BerrApplication {

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

}</code></pre>
<p><code>BaseTimeEntity</code>를 만들었다면 꼭 <code>Application</code>에 <code>@EnableJpaAuditing</code>을 추가해줘야 한다.</p>
<hr>
<h3 id="member">Member</h3>
<pre><code class="language-java">@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Entity
public class Member extends BaseTimeEntity {

    @Id @Column(name = &quot;member_id&quot;)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 45, unique = true)
    private String email;

    @Column(length = 45)
    private String nickname;

    private int age;

    @Column(length = 100)
    private String password;

    @Enumerated(EnumType.STRING)
    private Role role;

    public void encodePassword(PasswordEncoder passwordEncoder){
        this.password = passwordEncoder.encode(password);
    }
</code></pre>
<p>로그인할 <code>ID</code>는 <code>Email</code>이다.</p>
<hr>
<h3 id="role">Role</h3>
<pre><code class="language-java">public enum Role {
    USER, MANAGER, ADMIN;
}</code></pre>
<p><code>enum</code> 클래스인 것에 주의해서 설계하자</p>
<hr>
<h3 id="repository">Repository</h3>
<pre><code class="language-java">public interface MemberRepository extends JpaRepository&lt;Member, Long&gt; {

    Optional&lt;Member&gt; findByEmail(String email);
}
</code></pre>
<p><code>Extends</code>는 <code>JpaRepository&lt;엔티티, 엔티티 PK 타입&gt;</code>이다.
<code>findByEmail</code>은 JPA에서 지원하는 <code>쿼리 메소드</code>이며 이메일이 일치하는 <code>Member</code>를 찾아준다.</p>
<hr>
<h3 id="membersignuprequestdto">MemberSignUpRequestDto</h3>
<pre><code class="language-java">@Data
@Builder
@AllArgsConstructor
public class MemberSignUpRequestDto {

    @NotBlank(message = &quot;아이디를 입력해주세요&quot;)
    private String email;

    @NotBlank(message = &quot;닉네임을 입력해주세요.&quot;)
    @Size(min=2, message = &quot;닉네임이 너무 짧습니다.&quot;)
    private String nickname;

    @NotNull(message = &quot;나이를 입력해주세요&quot;)
    @Range(min = 0, max = 150)
    private int age;

    @NotBlank(message = &quot;비밀번호를 입력해주세요&quot;)
    @Pattern(regexp = &quot;^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&amp;])[A-Za-z\\d@$!%*#?&amp;]{8,30}$&quot;,
            message = &quot;비밀번호는 8~30 자리이면서 1개 이상의 알파벳, 숫자, 특수문자를 포함해야합니다.&quot;)
    private String password;

    private String checkedPassword;

    private Role role;

    @Builder
    public Member toEntity(){
        return Member.builder()
                .email(email)
                .nickname(nickname)
                .age(age)
                .password(password)
                .role(Role.USER)
                .build();
    }
}
</code></pre>
<hr>
<p>서비스 계층은 인터페이스를 분리해서 만들어보자</p>
<h3 id="memberservice">MemberService</h3>
<pre><code class="language-java">public interface MemberService {

    // 회원가입
    public Long signUp(MemberSignUpRequestDto requestDto) throws Exception;
}
</code></pre>
<hr>
<h3 id="memberserviceimpl">MemberServiceImpl</h3>
<pre><code class="language-java">@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    @Transactional
    @Override
    public Long signUp(MemberSignUpRequestDto requestDto) throws Exception {

        if (memberRepository.findByEmail(requestDto.getEmail()).isPresent()){
            throw new Exception(&quot;이미 존재하는 이메일입니다.&quot;);
        }

        if (!requestDto.getPassword().equals(requestDto.getCheckedPassword())){
            throw new Exception(&quot;비밀번호가 일치하지 않습니다.&quot;);
        }

        Member member = memberRepository.save(requestDto.toEntity());
        member.encodePassword(passwordEncoder);

        member.addUserAuthority();
        return member.getId();
    }
}
</code></pre>
<p><code>Exception</code> 핸들러는 없다치고 <code>Exception</code>을 사용하겠다.
Member에 미리 만들어 놓은 <code>encodePassword</code> 메서드를 사용해서 비밀번호를 <code>암호화</code>한다.</p>
<hr>
<h3 id="membercontroller">MemberController</h3>
<pre><code class="language-java">@RequiredArgsConstructor
@RequestMapping(&quot;/member&quot;)
@RestController
public class MemberController {

    private final MemberService memberService;
    private final MemberRepository memberRepository;

    @PostMapping(&quot;/join&quot;)
    @ResponseStatus(HttpStatus.OK)
    public Long join(@Valid @RequestBody MemberSignUpRequestDto request) throws Exception {
        return memberService.signUp(request);
    }</code></pre>
<p>이걸로 회원가입은 끝이났다. 로그인을 구현해보자.</p>
<hr>
<h2 id="로그인">로그인</h2>
<p>로그인을 하기 위해서 <code>SecurityConfig</code> 클래스를 만들어 줍니다.</p>
<pre><code class="language-java">@Configuration
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin().disable()
                .httpBasic().disable()
                .cors().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(&quot;/member/login&quot;).permitAll()
                .antMatchers(&quot;/member/join&quot;).permitAll()
                .antMatchers(&quot;/member&quot;).hasRole(&quot;USER&quot;)
                .anyRequest().authenticated();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    private static final String[] AUTH_WHITELIST = {
            &quot;/v2/api-docs&quot;,
            &quot;/v3/api-docs/**&quot;,
            &quot;/configuration/ui&quot;,
            &quot;/swagger-resources/**&quot;,
            &quot;/configuration/security&quot;,
            &quot;/swagger-ui.html&quot;,
            &quot;/webjars/**&quot;,
            &quot;/file/**&quot;,
            &quot;/image/**&quot;,
            &quot;/swagger/**&quot;,
            &quot;/swagger-ui/**&quot;,
            &quot;/h2/**&quot;
    };

    // 정적인 파일 요청에 대해 무시
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(AUTH_WHITELIST);
    }
}
</code></pre>
<hr>
<h3 id="securityutil">SecurityUtil</h3>
<pre><code class="language-java">public class SecurityUtil {
    public static String getLoginUsername(){
        UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        return user.getUsername();
    }
}</code></pre>
<hr>
<h3 id="customuserdetailsservice">CustomUserDetailsService</h3>
<pre><code class="language-java">@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

    private final MemberRepository memberRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return memberRepository.findByEmail(username)
                .orElseThrow(() -&gt; new UsernameNotFoundException(&quot;사용자를 찾을 수 없습니다.&quot;));
    }

}</code></pre>
<hr>
<h3 id="jwttokenprovider">JwtTokenProvider</h3>
<pre><code class="language-java">@RequiredArgsConstructor
@Component
public class JwtTokenProvider {
    private String secretKey =
            &quot;c2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQtc2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQK&quot;;

    // 토큰 유효시간 168 시간(7일)
    private long tokenValidTime = 1440 * 60 * 7 * 1000L;
    private final UserDetailsService userDetailsService;

    // 객체 초기화, secretKey 를 Base64로 인코딩합니다.
    @PostConstruct
    protected void init() {
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    }

    // JWT 토큰 생성
    public String createToken(String userPk, List&lt;String&gt; roles) {
        Claims claims = Jwts.claims().setSubject(userPk); // JWT payload 에 저장되는 정보단위
        claims.put(&quot;roles&quot;, roles); // 정보는 key/value 쌍으로 저장됩니다.
        Date now = new Date();
        return Jwts.builder()
                .setClaims(claims) // 정보 저장
                .setIssuedAt(now) // 토큰 발행 시간 정보
                .setExpiration(new Date(now.getTime() + tokenValidTime)) // set Expire Time
                .signWith(SignatureAlgorithm.HS256, secretKey)  // 사용할 암호화 알고리즘
                // signature 에 들어갈 secret 값 세팅
                .compact();
    }

    // JWT 토큰에서 인증 정보 조회
    public Authentication getAuthentication(String token) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token));
        return new UsernamePasswordAuthenticationToken(userDetails, &quot;&quot;, userDetails.getAuthorities());
    }

    // 토큰에서 회원 정보 추출
    public String getUserPk(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }

    // Request의 Header에서 token 값을 가져옵니다. &quot;X-AUTH-TOKEN&quot; : &quot;TOKEN값&#39;
    public String resolveToken(HttpServletRequest request) {
        return request.getHeader(&quot;X-AUTH-TOKEN&quot;);
    }

    // 토큰의 유효성 + 만료일자 확인
    public boolean validateToken(String jwtToken) {
        try {
            Jws&lt;Claims&gt; claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
            return !claims.getBody().getExpiration().before(new Date());
        } catch (Exception e) {
            return false;
        }
    }
}</code></pre>
<hr>
<h3 id="jwtauthenticationfilter">JwtAuthenticationFilter</h3>
<pre><code class="language-java">public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtAuthenticationProvider;

    public JwtAuthenticationFilter(JwtTokenProvider provider) {
        jwtAuthenticationProvider = provider;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = jwtAuthenticationProvider.resolveToken(request);

        if (token != null &amp;&amp; jwtAuthenticationProvider.validateToken(token)) {
            Authentication authentication = jwtAuthenticationProvider.getAuthentication(token);

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);

    }
}
</code></pre>
<hr>
<h3 id="memberservice-추가">MemberService 추가</h3>
<pre><code class="language-java">public String login(Map&lt;String, String&gt; members);</code></pre>
<hr>
<h3 id="memberserviceimpl-추가">MemberServiceImpl 추가</h3>
<pre><code class="language-java">@Override
public String login(Map&lt;String, String&gt; members) {

Member member = memberRepository.findByEmail(members.get(&quot;email&quot;))
        .orElseThrow(() -&gt; new IllegalArgumentException(&quot;가입되지 않은 Email 입니다.&quot;));

String password = members.get(&quot;password&quot;);
if (!member.checkPassword(passwordEncoder, password)) {
    throw new IllegalArgumentException(&quot;잘못된 비밀번호입니다.&quot;);
}

List&lt;String&gt; roles = new ArrayList&lt;&gt;();
roles.add(member.getRole().name());

return jwtTokenProvider.createToken(member.getUsername(), roles);</code></pre>
<hr>
<h3 id="membercontroller-1">MemberController</h3>
<pre><code class="language-java">@PostMapping(&quot;/login&quot;)
    public String login(@RequestBody Map&lt;String, String&gt; member) {
        return memberService.login(member);
    }</code></pre>
<hr>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/dd23714b-ce27-4a22-bab5-2505c1d7d3ba/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security docs로 공부하기 (#04)]]></title>
            <link>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-04</link>
            <guid>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-04</guid>
            <pubDate>Sun, 10 Jul 2022 08:44:49 GMT</pubDate>
            <description><![CDATA[<h2 id="authorities">Authorities</h2>
<p><code>Authentication</code>, 모든 구현이 개체 <code>Authentication</code>목록을 저장하는 방법을 설명한다. <code>GrantedAuthority</code>는 본인에게 부여된 권한을 나타낸다. </p>
<p>개체는 <code>GrantedAuthority</code> 개체에 삽입되고 나중에 권한 부여 결정을 내릴때 둘 중 하나에 의해 읽힌다.</p>
<p>아래는 <code>GrantedAuthority</code> 하나의 메서드만 있는 인터페이스다.</p>
<pre><code class="language-java">String getAuthority();</code></pre>
<p>이 방법을 사용하면 <code>Authorizationmanager</code>의 정확한<code>String</code> 표현을 얻을 수 있다.</p>
<hr>
<h2 id="pre-invocation-handling">Pre-Invocation Handling</h2>
<p><code>Spring Security</code>는 <code>메소드 호출</code> 또는 <code>웹 요청</code>과 같은 보안 객체에 대한 액세스를 제어하는 <code>인터셉터</code>를 제공한다.</p>
<h3 id="the-authorizationmanager">The AuthorizationManager</h3>
<p><code>AuthorizationManager</code> <code>AccessDecisionManager</code> 및 <code>AccessDecisionVoter</code>을 모두 대체한다.</p>
<p><code>AccessDecisionManager</code>를 사용 하도록 변경 하거나 사용자 지정하는 응용 프로그램이다.</p>
<p><code>AuthorizationManagers</code>는 <code>AuthorizationFilter</code>에 의해 호출되며 최종 액세스 제어 결정을 내릴 책임이 있다.</p>
<p><code>AuthorizationManager</code> 인터페이스에는 두 가지 메서드가 있다.</p>
<pre><code class="language-java">AuthorizationDecision check(Supplier&lt;Authentication&gt; authentication, Object secureObject);

default AuthorizationDecision verify(Supplier&lt;Authentication&gt; authentication, Object secureObject)
        throws AccessDeniedException {
}</code></pre>
<hr>
<h2 id="위임-기반-authorizationmanager-구현">위임 기반 AuthorizationManager 구현</h2>
<p>사용자가 <code>권한 부여</code>의 모든 측면을 제어하기 위해 자신의 것을 구현할 수 있지만, <code>Spring Security</code>는 <code>Authorizationmanager</code>를 위임한다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/be6d5d86-020a-4caf-9b60-321a1dd12245/image.png" alt=""></p>
<hr>
<h2 id="계층적-역할">계층적 역할</h2>
<p><code>계층적 역할</code>이라는 것은 <code>특정 역할</code>이 <code>다른 역할</code>을 <code>포함</code>하는 것이다.</p>
<p>예를 들어서 <code>USER</code>, <code>MANAGER</code>, <code>ADMIN</code> 3개의 권한이 있다.
(권한은 오름차순으로 높다.)</p>
<p>따라서 <code>MANAGER</code>는 <code>USER</code>의 권한을 가지고 있어야 하고, <code>ADMIN</code>은 <code>USER</code>, <code>MANAGER</code> 2개의 권한을 모두 다 가지고 있어야 한다.</p>
<p>이러한 계층적 역할을 지원하는 <code>RoleVoter</code>은 <code>RoleHierarchyVoter</code>로 구성되어 <code>RoleHierarchy</code> 사용자에게 할당된 모든 <code>접근 가능한 권한</code>을 얻는다.</p>
<pre><code class="language-java">@Bean
AccessDecisionVoter hierarchyVoter() {
    RoleHierarchy hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy(&quot;ROLE_ADMIN &gt; ROLE_STAFF\n&quot; +
            &quot;ROLE_STAFF &gt; ROLE_USER\n&quot; +
            &quot;ROLE_USER &gt; ROLE_GUEST&quot;);
    return new RoleHierarchyVoter(hierarchy);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security docs로 공부하기 (#03)]]></title>
            <link>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-03-xf581u7x</link>
            <guid>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-03-xf581u7x</guid>
            <pubDate>Sun, 10 Jul 2022 07:45:40 GMT</pubDate>
            <description><![CDATA[<p><code>Spring Security</code>는 <code>인증</code>에 대한 포괄적인 지원을 제공한다.</p>
<hr>
<h2 id="authentication-mechanisms">Authentication Mechanisms</h2>
<ol>
<li>사용자 이름 및 비밀번호 - 사용자 이름/비밀번호로 인증하는 방법</li>
<li>OAuth 2.0 로그인 - <code>OAuth 2.0 OpenID Connect</code> 및 <code>비표준 OAuth 2.0</code> 로그인 (ex: 네이버 로그인, 카카오 로그인, 깃허브 로그인 ..)</li>
<li>SAML 2.0 로그인 : SAML 2.0 로그인</li>
<li>중앙 인증 서버(CAS) : 중앙 인증 서버 지원</li>
<li>RememberMe : 세션 만료 후 사용자 정보를 기억함</li>
<li>JAAS 인증 : <code>JAAS</code>로 인증</li>
<li>OpenID : <code>OpenID</code> 인증(<code>OpenID Connect</code>와 혼동 X)</li>
<li>사전 인증 시나리오 : <code>SiteMinder</code> 또는 Java EE 보안과 같은 외부 매커니즘으로 인증을 하지만 일반적인 악용에 대한 권한 부여 및 보호를 위해 여전히 <code>Spring Security</code>를 사용</li>
<li>X509 인증 : X509 인증</li>
</ol>
<p>간단하게 스프링 시큐리티 내에서 <strong>폼 기반 로그인</strong>이 어떻게 작동하는지 살펴보자.</p>
<hr>
<h2 id="form-login">Form Login</h2>
<p>Spring Security는 html 형식을 통해 제공되는 사용자 이름과 비밀번호에 대한 지원을 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/4eaee4c8-8698-471a-a7d2-2ae56b5906ab/image.png" alt=""></p>
<h3 id="인증되지-않은-상태로그인-x에서-권한이-필요한-리소스에-접근하려고-할-때">인증되지 않은 상태(로그인 X)에서 권한이 필요한 리소스에 접근하려고 할 때</h3>
<ol>
<li>먼저 사용자가 권한이 없는 리소스 <code>/private</code>에 대해 인증되지 않은 요청을 한다.</li>
<li><code>Spring Security</code>는 인증 <code>FilterSecurityinterceptor</code>되지 않은 요청이 <code>AccessDeninedException</code>에 의해서 <code>ExceptionTranslationFilter</code>로 반환된다.</li>
<li>사용자가 인증되지 않았으므로 로그인 페이지로 <code>리디렉션</code>한다.</li>
<li>브라우저는 <code>리디렉션</code>된 로그인 페이지를 요청한다.</li>
<li>애플리케이션 내에서 로그인 페이지를 <code>렌더링</code> 한다.</li>
</ol>
<p>로그인 페이지가 렌더링 되고 사용자가 <code>사용자 이름</code>과 <code>암호</code>를 제출하면 <code>UsernamePasswordAuthenticationFilter</code>로 인증한다.
<img src="https://velog.velcdn.com/images/nyong_i/post/a04b2b44-7c10-4590-bce9-aff4a50b008c/image.png" alt=""></p>
<h3 id="로그인-과정">로그인 과정</h3>
<ol>
<li><code>사용자 이름</code>과 <code>암호</code>를 추출하여 <code>UsernamePasswordAuthenticationFilter</code>를 거친다.</li>
<li><code>UsernamePasswordAuthenticationToken</code>으로 전달된다.</li>
<li>인증에 실패하면 <code>Failure</code><ul>
<li><code>SecurityContextHolder</code>가 지워진다.</li>
<li><code>RememberMeServices.loginFail</code>이 호출된다.</li>
<li><code>AuthenticationFailureHandler</code>가 호출된다.</li>
</ul>
</li>
<li>인증에 성공하면 <code>Success</code><ul>
<li><code>SessionAuthenticationStrategy</code>에서 새로운 로그인 알림을 받음</li>
<li><code>RememberMeServices.loginSuccess</code> 호출</li>
<li><code>ApplicationEventPulbisher</code> 발행</li>
<li><code>AuthenticationSuccessHandler</code> 호출 일반적으로 이것은 로그인 페이지로 리디렉션할 때 <code>SimpleUrlAuthenticationSuccessHandler</code> 저장된 요청으로 리디렉션</li>
</ul>
</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security docs로 공부하기 (#02)]]></title>
            <link>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-02</link>
            <guid>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-02</guid>
            <pubDate>Sun, 10 Jul 2022 07:02:55 GMT</pubDate>
            <description><![CDATA[<p>이번 섹션에서는 <code>Servlet</code> 기반 애플리케이션 내에서 <code>Spring Security</code>의 <code>고수준 아키텍쳐</code>에 대해서 설명한다.</p>
<p><code>참조의 인증</code>, <code>권한 부여</code>, <code>악용에 대한 보호</code>를 사용할 때 아키텍쳐에 대한 높은 수준의 이해를 요구한다고 한다.</p>
<hr>
<h2 id="a-review-of-filters">A Review of Filters</h2>
<p><code>Spring Security</code>의 <code>Servlet</code> 지원은 <code>Servlet</code>을 기반으로 하므로 <code>Filter</code>의 역할을 먼저 살펴보는 것이 도움이 된다.</p>
<p>아래의 <code>Filter</code> 그림은 단일 <code>HTTP</code> 요청에 대한 처리기의 일반적인 계층화를 보여준다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/e523b85b-4340-4aa1-96f7-114c48009020/image.png" alt=""></p>
<p>클라이언트는 애플리케이션에 요청을 보내고 컨테이너는 <code>FilterChain</code>를 포함하고 <code>요청 URI</code>의 경로를 기반으로 처리한다.</p>
<hr>
<h2 id="delegatingfilterproxy">DelegatingFilterProxy</h2>
<p><code>Spring</code>은 Servlet <code>Filter</code>와 <code>DelegatingFilterProxy</code>컨테이너의 라이프사이클과 Spring의 <code>ApplicationContext</code>를 지원한다.</p>
<p><code>DelegatingFilterProxy</code>는 <code>서블릿 컨테이너</code>와 <code>스프링 컨테이너(어플리케이션 컨텍스트)</code> 사이의 링크를 제공하는 <code>ServletFilter</code>이다.
특정한 이름을 가진 스프링 빈을 찾아 그 빈에게 요청을 <code>위임</code>한다.</p>
<p><strong>Why?</strong> <code>서블릿 필터(서블릿 컨테이너)</code>와 <code>시큐리티 필터(스프링 컨테이너)</code>는 서로 다른 컨테이너에서 생성되고 동작하기 때문에 이를 연결해줄 것이 필요함</p>
<p>다음은 <code>DelegatingFilterProxy</code>의 동작 방식이다.
<img src="https://velog.velcdn.com/images/nyong_i/post/96443098-835c-4520-b462-d4d99ed1c56f/image.png" alt=""></p>
<p><code>DelegatingFilterProxy</code>에서 <code>Bean Filter</code>를 찾은 다음 <code>ApplicationContext</code>를 호출한다.</p>
<p>무슨말인지 잘 이해가 안될 수 있지만 아래 그림을 보면 이해가 좀 더 쉬울 것이다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/aff22d45-eacc-4750-86a5-4d34f7ab8e61/image.png" alt=""></p>
<p>참고로 <code>DispatcherServlet</code>보다 <code>DelegatingFilterProxy</code>의 우선순위가 더 높다.</p>
<p>따라서 <code>DispatcherServlet</code>이 요청을 다 가로채갈일은 없다고 보면된다.</p>
<h3 id="delegatingfilterproxy-의사-코드">DelegatingFilterProxy 의사 코드</h3>
<pre><code class="language-java">public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    Filter delegate = getFilterBean(someBeanName);
    delegate.doFilter(request, response);
}</code></pre>
<p>정리해서 <code>DelegatingFilterProxy</code>까지 적용된 요청의 동작 순서는 <code>Request</code>가 <code>Filter</code>(Servlet 컨테이너)를 거치다가 <code>DelegatingFilterProxy</code>를 만나면 <code>ApplicationContext</code>(Spring 컨테이너)로 이동해서 인증을 마저 진행한다.</p>
<p>사진으로 보면 다음과 같다.
<img src="https://velog.velcdn.com/images/nyong_i/post/cfb7c011-7ac5-4ea3-a2e4-3034a84bc8b0/image.png" alt=""></p>
<hr>
<h2 id="filterchainproxy">FilterChainProxy</h2>
<p><code>Spring Security</code>의 <code>Servlet</code> 지원은 <code>FilterChainProxy</code>를 통해 많은 인스턴스에 위임할 수 있는 특수한 기능도 포함되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/87823c0f-292a-483f-9781-2219a4a9681a/image.png" alt=""></p>
<p><code>Spring 컨테이너</code>로 도착한 요청은 <code>DelegatingFilterProxy</code>로부터 요청을 <code>위임</code> 받고 실제로 보안을 처리한다.</p>
<p>사용자의 요청을 필터 순서대로 호출하여 전달한다. 사용자 정의 필터를 생성해서 기존의 필터 전후로 추가 가능하다.</p>
<p><strong>마지막 필터</strong>까지 <code>인증</code> 및 <code>인가</code> <strong>예외가 발생하지 않으면</strong> 보안을 통과한다.</p>
<hr>
<h2 id="securityfilterchain과-security-filters">SecurityFilterChain과 Security Filters</h2>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/b25c7192-035c-474c-83d8-46309f36b6b1/image.png" alt=""></p>
<p><code>FilterChainProxy</code>에서 잠깐 살펴 봤던것과 같이 <strong>마지막 필터</strong>까지 <code>인증</code> 및 <code>인가</code> <strong>예외가 발생하지 않으면</strong> 보안을 통과한다.</p>
<p>스프링 시큐리티에서 제공하는 필터는 다음과 같다.</p>
<ul>
<li>ForceEagerSessionCreationFilter</li>
<li>채널 처리 필터</li>
<li>WebAsyncManagerIntegrationFilter</li>
<li>SecurityContextPersistenceFilter</li>
<li>헤더라이터 필터</li>
<li>CorsFilter</li>
<li>CSRF 필터</li>
<li>로그아웃 필터</li>
<li>OAuth2AuthorizationRequestRedirectFilter</li>
<li>Saml2WebSso 인증 요청 필터</li>
<li>X509 인증 필터</li>
<li>AbstractPreAuthenticatedProcessingFilter</li>
<li>CasAuthenticationFilter</li>
<li>OAuth2Login 인증 필터</li>
<li>Saml2WebSso 인증 필터</li>
<li>UsernamePasswordAuthenticationFilter</li>
<li>OpenID인증 필터</li>
<li>기본 로그인 페이지 생성 필터</li>
<li>기본 로그아웃 페이지 생성 필터</li>
<li>동시 세션 필터</li>
<li>DigestAuthenticationFilter</li>
<li>BearerTokenAuthenticationFilter</li>
<li>BasicAuthenticationFilter</li>
<li>요청 캐시 인식 필터</li>
<li>SecurityContextHolderAwareRequestFilter</li>
<li>JaasApiIntegrationFilter</li>
<li>RememberMeAuthenticationFilter</li>
<li>익명 인증 필터</li>
<li>OAuth2AuthorizationCodeGrantFilter</li>
<li>세션 관리 필터</li>
<li>ExceptionTranslationFilter</li>
<li>FilterSecurityInterceptor</li>
<li>SwitchUserFilter</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security docs로 공부하기 (#01)]]></title>
            <link>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-01</link>
            <guid>https://velog.io/@nyong_i/Spring-Security-docs%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-01</guid>
            <pubDate>Sun, 10 Jul 2022 05:47:49 GMT</pubDate>
            <description><![CDATA[<p><code>스프링 시큐리티</code>를 처음 접하는 사람들이 정확하게 이해해서 <code>인증</code>, <code>인가</code> 처리를 구현하는 것은 쉽지 않다.</p>
<p>나 또한 스프링 시큐리티의 진입 장벽이 낮지 않다고 생각하고 단순히 구글링해서 클론코딩으로만 <code>인증</code>, <code>인가</code>를 구현하는 것은 썩 좋지 않다고 생각한다.</p>
<p>때문에 이번 기회에 <a href="https://docs.spring.io/spring-security/reference/servlet/getting-started.html">Spring Security docs</a>를 읽어보면서 스프링 시큐리티의 <code>동작원리</code>, <code>인증</code>, <code>인가</code> 등등 자세히 알아보자.</p>
<hr>
<h2 id="updating-dependencies">Updating Dependencies</h2>
<p>스프링 시큐리티를 사용하려면 <code>Maven</code> 또는 <code>Gradle</code>을 이용하여 종속성을 업데이트 하면 된다고 한다.</p>
<h3 id="maven">Maven</h3>
<pre><code class="language-xml">&lt;dependencies&gt;
    &lt;!-- ... other dependency elements ... --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code></pre>
<h3 id="gradle">Gradle</h3>
<pre><code class="language-java">dependencies {
    compile &quot;org.springframework.boot:spring-boot-starter-security&quot;
}</code></pre>
<p>의존성을 추가한 뒤 서버를 실행시키면 아래와 같은 화면이 나타난다.
<img src="https://velog.velcdn.com/images/nyong_i/post/c27d2f89-3a6b-4243-b266-029d74293949/image.png" alt=""></p>
<hr>
<h2 id="spring-boot-auto-configuration">Spring Boot Auto Configuration</h2>
<p><code>Spring Boot</code>는 자동으로 <code>Spring Security</code>의 기본 구성(서블릿 <code>Filter</code>와 <code>springSecurityFilterChain</code>)을 활성화한다.</p>
<p><code>Filter</code>와 <code>springSecurityFilterChain</code>은 <code>빈</code>이며, 이는 응용 프로그램 내에 모든 보안(<code>응용 프로그램 URL 보호</code>, <code>사용자 이름 및 비밀번호 확인</code>, <code>로그인 양식으로 리디렉션</code> 등)을 담당한다고 한다.</p>
<p>Spring Boot는 많은 설정을 하지는 않지만 많은 작업을 수행할 수 있다.(Spring Security 덕분에)</p>
<h4 id="기능요약은-다음과-같다">기능요약은 다음과 같다.</h4>
<ul>
<li>응용 프로그램과의 모든 상호 작용을 위해 인증된 사용자 필요</li>
<li>기본 로그인 양식 생성</li>
<li>사용자 이름 <code>user</code>와 콘솔에서 제공하는 비밀번호를 사용하여 인증</li>
<li><code>BCrypt</code>를 사용해서 비밀번호를 <code>암호화</code></li>
<li>로그아웃</li>
<li><code>CSRF</code> 공격 방지</li>
<li>세션 고정 보호</li>
<li>보안 헤더 통합<ul>
<li>보안 요청을 위한 HTTP Strict Transport Security</li>
<li>X-Content-type-Options 통합</li>
<li>캐시 제어(나중에 애플리케이션에서 재정의하여 정적 리소스를 캐싱할  수 있음)</li>
<li>X-XSS-Protection 통합</li>
<li>클릭재킹 방지를 위한 X-Frame-Options 통합</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[동시성과 병렬성 차이가 뭔지 아세요?]]></title>
            <link>https://velog.io/@nyong_i/ttiw2pf5</link>
            <guid>https://velog.io/@nyong_i/ttiw2pf5</guid>
            <pubDate>Wed, 06 Jul 2022 08:12:19 GMT</pubDate>
            <description><![CDATA[<p>동시성과 병렬성은 헷갈릴 수 있는 주제이다. 사전상의 의미도 같을 뿐더러 컴퓨터 분야에서 말하는 동시성과 병렬성의 차이점을 확실하게 이해하기도 난해하다.</p>
<p>아래에서 자세하게 살펴보자</p>
<hr>
<h2 id="동시성과-병렬성">동시성과 병렬성</h2>
<p><code>동시성</code>이란 <code>싱글 코어</code>에서 <code>멀티 쓰레드</code>를 동작 시키는 방식이다.
(코어의 종류에는 <code>싱글 코어</code>와 <code>멀티 코어</code>가 있다.)</p>
<ul>
<li>싱글 코어 - 동시성</li>
<li>멀티 코어 - 병렬성</li>
</ul>
<blockquote>
<p>싱글 코어에서 멀티쓰레드를 동작 시키는 방식은 동시성,
멀티 코어에서 멀티쓰레드를 동작 시키는 방식은 병렬성이다.</p>
</blockquote>
<p>따라서 이렇게 이해하면 좀 와닿을 수 있다.</p>
<p><code>동시성</code>과 <code>병렬성</code>은 사전에서는 같은 의미로 사용하고 있지만, 컴퓨터 분야에서는 다르게 정의하고 있기 때문에 이 둘을 서로 비교해가며 알아보자</p>
<table>
<thead>
<tr>
<th>동시성</th>
<th>병렬성</th>
</tr>
</thead>
<tbody><tr>
<td>동시에 실행되는 것 <strong>같아 보이는 것</strong></td>
<td><strong>실제로</strong> 동시에 여러 작업이 처리되는 것</td>
</tr>
<tr>
<td><strong>싱글 코어</strong>에서 멀티 쓰레드를 동작 시키는 방식</td>
<td><strong>멀티 코어</strong>에서 멀티 쓰레드를 동작 시키는 방식</td>
</tr>
<tr>
<td>논리적인 개념</td>
<td>물리적인 개념</td>
</tr>
</tbody></table>
<p>표로만 봐도 뭔가 다르다는게 느껴지긴 한다. </p>
<p>하지만 뭔가 잘 이해가 되지 않을 수도 있다. 동시에 실행되는 것도 아니고, <strong>실행 되는 것 같아 보이는 것</strong>은 무슨 말인가;</p>
<p>아래의 그림을 통해 알아보자 </p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/1ddc89da-873c-485c-bae9-e4fc398bb5c6/image.jpeg" alt=""></p>
<p><code>Concurrent</code>는 <code>동시성</code>이다.</p>
<p>그림과 같이 2개의 작업이 번갈아 가며 진행되면서 동시에 진행되는 것 처럼 보입니다. 그래서 <strong>동시에 실행되는 것 같아 보이는 것</strong>이라고 표현한다.</p>
<p>반면 <code>Parrallel(병렬성)</code>은 실제로 작업을 동시에 진행하는 것을 볼 수 있다.</p>
<p>실제로 4개의 코어를 가진 <code>쿼드 코어</code>의 속도는 이론적으로 실행 속도를 <code>4배</code>까지 향상시킬 수 있다.(물론 오버헤드로 인해 실제 4배가 되기는 어렵다.)</p>
<p>이렇게 동시성과 병렬성을 간단하게 알아보았다.</p>
<hr>
<h2 id="정리">정리</h2>
<ul>
<li><p>병렬성이 <code>멀티 코어</code> + <code>멀티 스레드</code> 작업이라 항상 더 좋을 것 같지만, <code>동시성</code>으로 접근하는게 좋은 경우도 있음 ex) <code>네트워크 통신</code>, <code>파일 저장 및 로드</code> 등의 <code>I/O</code> 작업은 <code>CPU</code>가 거의 일을 하지 않고 요청 후 응답이 올 때까지 <code>대기상태</code>에 있게됨. 이때 한 개의 <code>CPU</code>가 <code>I/O</code> 요청 후 기다리는 동안 다른 작업을 처리하도록 하면 효율적임</p>
</li>
<li><p><code>동시성</code>과 <code>병렬성</code>을 혼용해서 처리하는 경우도 있음</p>
</li>
<li><p><code>동시성</code>은 작업이 바뀔 때 <code>문맥 교환</code>이 발생하고, 동시 작업이 너무 많다면 <code>문맥 교환</code>의 <code>오버헤드</code>로 인해 <code>싱글 코어</code>에서 <code>싱글 스레드</code>로 작업하는 것이 더 빠를 수 있음</p>
</li>
<li><p>코어가 N배로 늘어나더라고 성능이 N배로 늘어나는 것은 아님(암달의 법칙) : 프로그램의 모든 부분을 <code>Parallel</code> 하게 작성할 수 없고 반드시 <code>Sequential</code>하게 동작해야 하는 부분들이 존재함</p>
</li>
</ul>
<hr>
<ul>
<li><a href="https://mentha2.tistory.com/245">https://mentha2.tistory.com/245</a></li>
<li><a href="https://seamless.tistory.com/42">https://seamless.tistory.com/42</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SpringBoot에서 Thread 풀ㄹㄹㄹㄹ 건드려보기]]></title>
            <link>https://velog.io/@nyong_i/Thread-%ED%92%80%E3%84%B9%E3%84%B9%E3%84%B9%E3%84%B9</link>
            <guid>https://velog.io/@nyong_i/Thread-%ED%92%80%E3%84%B9%E3%84%B9%E3%84%B9%E3%84%B9</guid>
            <pubDate>Wed, 06 Jul 2022 07:23:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://bunny.jjalbot.com/2018/12/drfsQIDwHz/zzal.gif" alt=""></p>
<p><code>스레드 풀</code>이 무엇이고 자바에서 <code>스레드 풀</code>을 가지고 어떻게 <code>스레드</code>를 관리하는지 알아보자</p>
<hr>
<h2 id="executor와-스레드-풀">Executor와 스레드 풀</h2>
<p>자바 5는 자바 프로그래머가 <code>태스크 제출</code>과 <code>실행</code>을 분리할 수 있는 기능을 제공했다.</p>
<hr>
<h3 id="스레드의-문제---스레드-풀을-사용해야-하는-이유">스레드의 문제 - 스레드 풀을 사용해야 하는 이유</h3>
<p><code>자바 스레드</code>는 직접 운영체제 스레드에 접근한다. 운영체제 스레드를 만들고 종료하려면 <code>비싼 비용</code>을 치러야 한다. </p>
<p>하지만 운영체제의 스레드 숫자는 제한되어있다. 운영체제가 지원하는 스레드 수를 초과해 사용하면 자바 애플리케이션이 예상치 못한 방식으로 <code>크러시</code>될 수 있으므로 기존 스레드가 실행되는 상태에서 계속 새로운 스레드를 만드는 상황이 일어나지 않도록 주의해야 한다.</p>
<hr>
<h3 id="🙄-스레드-풀이-뭔데">🙄 스레드 풀이 뭔데?</h3>
<p>위에서 스레드를 그냥 가져다 쓰면 무슨 일이 일어나는지 언급했다. 따라서 이 문제점들을 해결하기 위해서 스레드 풀을 사용해야 한다.
(기본적으로 스프링 부트에서 제공하는 <code>tomcat</code>은 스레드 풀을 지원한다.)</p>
<p>자바 <code>ExecutorService</code>는 태스크를 제출하고 나중에 결과를 수집할 수 있는 인터페이스를 제공한다. 프로그램은 <code>newFixedThreadPool</code>같은 <code>팩토리 메서드</code>중 하나를 이용해 <code>스레드 풀</code>을 만들어 사용할 수 있다.</p>
<pre><code class="language-java">ExecutorService newFixedThreadPool(int nThreads)</code></pre>
<p>위의 메서드는 <code>워커 스레드</code>라 불리는 <code>nThreads</code>를 포함하는 <code>ExecutorService</code>를 만들고 이들을 <code>스레드 풀</code>에 저장한다.</p>
<p>스레드 풀에서 사용하지 않은 스레드로 제출된 태스크를 먼저 온 순서대로 실행한다. 이들 태스크 실행이 종료되면 이들 스레드를 풀로 반환한다.</p>
<p>하지만 우리는 <code>웹 백엔드 개발자</code>가 아닌가
위에서 나온 <code>JAVA 5</code>에서 팩토리 메서드를 사용해가며 스레드 풀을 만들 필요 없다.</p>
<p>아래 코드처럼 <code>yml</code>에 서버의 스레드 풀을 직접 관리해도 좋고 당연한 소리지만 <code>properties</code>에서 관리해도 좋다.</p>
<pre><code class="language-yml">server:
  tomcat:
    threads:
      max: 200 
      min-spare: 10 
    max-connections: 8192 
    accept-count: 100
    connection-timeout: 20000 
  port: 8080 </code></pre>
<ul>
<li>max : 쓰레드의 최대 개수</li>
<li>min-spare : 활성화 상태로 유지할 최소 쓰레드의 개수</li>
<li>max-connections : 수립가능한 connection의 총 개수</li>
<li>accept-count : 모든 스레드가 사용 중일때 들어오는 연결 요청 큐의 최대 길이</li>
<li>connection-timeout : timeout 판단 기준 시간, 20초</li>
</ul>
<p>위 내용이 무슨 뜻인지 아래에서 더 자세하게 살펴보자.</p>
<p>아래 사진은 스레드 풀에 스레드 200개가 있는 모습이다.
<img src="https://velog.velcdn.com/images/nyong_i/post/7b3fbdd6-a78a-472a-a61c-a8dd8932daeb/image.png" alt=""></p>
<p>스레드 풀에 스레드가 없는데 계속 요청을 보내면 이미 스레드를 사용하고 있던 <code>reqeust</code>가 스레드를 반환해 줄 때까지 기다리거나, 거절하는 모습이다.</p>
<p>이 같은 모습은 <code>request</code>가 스레드를 사용하고 다시 스레드 풀로 반환해야 한다는 것을 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/ff90b863-58a6-416f-916e-786a280217fd/image.png" alt=""></p>
<p>스레드 풀의 특징은 다음과 같다</p>
<ul>
<li>필요한 <code>스레드</code>를 <code>스레드 풀</code>에 <code>보관</code>하고 <code>관리</code>한다.</li>
<li><code>스레드 풀</code>에 생성 가능한 <code>스레드</code>의 <code>최대치</code>를 관리한다.</li>
<li><code>톰캣</code>은 최대 <code>200개</code>가 기본 설정이며 변경 가능하다.</li>
<li>스레드가 필요하면 이미 생성되어있는 스레드를 스레드 풀에서 꺼내 사용한다.</li>
<li>사용을 종료하면 스레드 풀에 해당 <code>스레드</code>를 <code>반납</code>한다.</li>
<li>최대 스레드가 모두 사용중이어서 스레드 풀에 스레드가 없다면 기다리는 요청은 거절하거나 특정 숫자만큼만 대기하도록 설정할 수 있다.</li>
</ul>
<p>마지막 특징은 스레드 풀에 스레드가 0개이면 대기하는 <code>request</code>의 수를 정할 수 있다는 말이다.</p>
<p><code>스레드 풀</code>의 궁극적인 <code>장점</code>은 하드웨어에 맞는 수의 <code>태스크</code>를 <code>유지함</code>과 동시에 <code>수 천개</code>의 <code>태스크(request)</code>를 <code>스레드 풀</code>에 아무 <code>오버헤드</code> 없이 제출할 수 있다는 점이다.</p>
<p><code>WAS</code>의 주요 튜닝 포인트는 <code>최대 스레드 수</code>이다.</p>
<blockquote>
<p>값을 너무 낮게 설정하면?
동시 요청이 많을 때 서버 리소스는 여유롭지만 클라이언트는 금방 응답이 지연된다.</p>
</blockquote>
<blockquote>
<p>값을 너무 높게 설정하면?
동시 요청이 많으면 CPU, 메모리 리소스 임계점 초과로 서버가 다운된다.</p>
</blockquote>
<p>스레드 풀의 최적의 스레드 수를 고려하고 있다면 <code>nGrider</code>로 성능 테스트를 해보는 것을 추천한다.</p>
<hr>
<h3 id="references">references</h3>
<ul>
<li><p><a href="https://velog.io/@sihyung92/how-does-springboot-handle-multiple-requests">https://velog.io/@sihyung92/how-does-springboot-handle-multiple-requests</a></p>
</li>
<li><p><a href="https://jaehoney.tistory.com/155">https://jaehoney.tistory.com/155</a></p>
</li>
<li><p><a href="https://dolphinsarah.tistory.com/55?category=497941">https://dolphinsarah.tistory.com/55?category=497941</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내가 만드는 API를 REST"ful" 하게]]></title>
            <link>https://velog.io/@nyong_i/%EB%A1%9C%EC%9D%B4-%ED%95%84%EB%94%A9%EC%9D%B4-%EC%9D%B4-%EA%B8%80%EC%9D%84-%EC%A2%8B%EC%95%84%ED%95%A9%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@nyong_i/%EB%A1%9C%EC%9D%B4-%ED%95%84%EB%94%A9%EC%9D%B4-%EC%9D%B4-%EA%B8%80%EC%9D%84-%EC%A2%8B%EC%95%84%ED%95%A9%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Sat, 11 Jun 2022 08:29:12 GMT</pubDate>
            <description><![CDATA[<h2 id="rest">REST</h2>
<p><strong>RE</strong>presentational
<strong>S</strong>tate
<strong>T</strong>ransfer</p>
<p>컴퓨터 시스템간의 상호 운영성을 제공하는 방법 중 하나이다.</p>
<h3 id="web1991">WEB(1991)</h3>
<blockquote>
<p>&quot;어떻게 인터넷에서 정보를 공유할 것인가?&quot; (인터넷만 존재하는 시기)</p>
</blockquote>
<p>가 기존 인터넷들의 문제점들 중 하나였다. 이에 대한 해답으로 <strong>WEB</strong>이 등장함 (월드 와이드 웹(WWW)이 <a href="https://ko.wikipedia.org/wiki/%ED%8C%80_%EB%B2%84%EB%84%88%EC%8A%A4%EB%A6%AC?tableofcontents=1">팀 버너스-리</a>에 의해 탄생)
<img width="257" alt="스크린샷 2022-06-09 오전 8 13 24" src="https://user-images.githubusercontent.com/94087228/172732741-215f926f-8639-4da7-97d4-69b78a3c786b.png"></p>
<blockquote>
<p>&quot;정보들을 하이퍼텍스트로 연결하자&quot;</p>
<ul>
<li>표현 형식 : HTML</li>
<li>식별자 : URI</li>
<li>전송 방법 : HTTP</li>
</ul>
</blockquote>
<p>위의 명세를 바탕으로 HTTP/1.0(1994~1996) 프로토콜을 개발하게 됨(<em>기존에 사용하고 있던 HTTP에서 더욱 명세에 기능을 더하고 기존의 기능을 고친</em>)</p>
<h4 id="roy-t-fielding19941996">Roy T. Fielding(1994~1996)</h4>
<blockquote>
<p>&quot;어떻게 하면 기존에 HTTP로 구축되어 있던 웹들과 호환성의 문제가 생기지 않게 개발할 수 있을까?&quot;</p>
</blockquote>
<h3 id="http-object-model1994">HTTP Object Model(1994)</h3>
<p>Roy T. Fielding이 제기했던 문제점들을 해결할 수 있는 방안</p>
<h2 id="rest의-공식-발표1998-2000">REST의 공식 발표(1998, 2000)</h2>
<p>(1998) Roy T. Fielding이 Microsoft Research에서 HTTP Object Model을 <strong>REST</strong>이라는 이름으로 발표</p>
<p>(2000) 박사급 논문으로 발표</p>
<p>이 때 발표한 논문이 우리가 오늘날 알고 있는 <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">REST를 정의하는 논문</a>이다.</p>
<hr>
<h2 id="api">API</h2>
<p>인터넷상에 API라는 것이 만들어지기 시작함</p>
<p>1998년에 마이크로소프트가 <strong>XML-RPC</strong>라는 원격으로 다른 시스템의 메서드를 호출할 수 있는 프로토콜을 만듦 -&gt; <strong>SOAP</strong></p>
<h3 id="salesforce-api2000---soap-형태---매우-복잡하고-어려움">Salesforce API(2000) - <strong>SOAP 형태</strong> - 매우 복잡하고 어려움</h3>
<p><img src="https://user-images.githubusercontent.com/94087228/172734847-e46bed92-c7d8-4445-862f-39b996360712.png" alt="image"></p>
<h3 id="filckr2004---soap-형태--rest-형태---soap보다-매우-쉽고-간결해짐">filckr(2004) - <strong>SOAP 형태 &amp;&amp; REST 형태</strong> - SOAP보다 매우 쉽고 간결해짐</h3>
<p><img src="https://user-images.githubusercontent.com/94087228/172738444-9e3e7e87-d255-4846-b686-ff02bc1a2a10.png" alt="image"></p>
<h3 id="사용자들이-생각한-soap와-rest의-차이점">사용자들이 생각한 SOAP와 REST의 차이점</h3>
<table>
<thead>
<tr>
<th>SOAP</th>
<th>REST</th>
</tr>
</thead>
<tbody><tr>
<td>복잡하고</td>
<td>단순하고</td>
</tr>
<tr>
<td>규칙많고</td>
<td>규칙적고</td>
</tr>
<tr>
<td>어려운</td>
<td>쉬운</td>
</tr>
</tbody></table>
<h3 id="결과">결과</h3>
<img width="524" alt="스크린샷 2022-06-09 오전 9 21 16" src="https://user-images.githubusercontent.com/94087228/172738712-f40aecfb-ee74-43a3-ac9f-2ba76355520e.png">

<hr>
<h2 id="월드-와이드-웹www이-rest-api로-정착하기-위해서">월드 와이드 웹(WWW)이 REST API로 정착하기 위해서</h2>
<h3 id="cmis-등장2008">CMIS 등장(2008)</h3>
<ul>
<li><a href="https://ecommerce-platforms.com/ko/glossary/content-management-system-cms">CMS</a>를 위한 표준</li>
<li>EMC, IBM, Microsoft 등이 함께 작업</li>
<li><strong>REST 바인딩 지원</strong></li>
</ul>
<blockquote>
<p>Roy T. Fielding : &quot;CMIS에 <strong>REST</strong>는 존재하지 않는다.&quot;</p>
</blockquote>
<h3 id="microsoft-rest-api-guidelines2016">Microsoft REST API Guidelines(2016)</h3>
<p>마이크로소프트가 REST API에 대한 구체적인 <a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md">가이드라인</a> 제시</p>
<ul>
<li><a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md#92-serialization">URI는 https://{serviceRoot}/{collection}/{id} 형식이어야 한다.</a></li>
<li><a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md#74-supported-methods">GET, PUT, DELETE, POST, HEAD, PATCH, OPTIONS를 지원해야 한다.</a></li>
<li>API <a href="https://wiserloner.tistory.com/466">버저닝</a>은 <a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md#121-versioning-formats">Major.minor로 하고 URI에 버전 정보를 포함시켜야 한다.</a></li>
<li><a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md#7-consistency-fundamentals">쉽게 이해할 수 있도록 일관성을 지켜야한다.</a></li>
<li><a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md#11-json-standardizations">기본 유형에 대한 JSON 형식의 기본 값은 RFC4627 규칙에 따라 JSON으로 직렬화되어야 한다.</a></li>
<li>....</li>
</ul>
<blockquote>
<p>Roy T. fielding : &quot;이것은 <strong>REST API</strong>가 아니라, <strong>HTTP API</strong>라고 해야한다.&quot;</p>
</blockquote>
<hr>
<blockquote>
<p>Roy T. fielding : &quot;이런것들(CMIS, Microsoft REST API Guidelines)을 REST라고 부르려면 <a href="https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven"><strong>&quot;hypertext-driven&quot;</strong></a> 이어야 하며, REST API를 위한 최고의 버저닝 전략은 버저닝을 안 하는 것이다.&quot;</p>
<ul>
<li>hypertext : 컴퓨터나 다른 전자기기로 한 문서를 읽다가 다른 문서로 순식간에 이동해 읽을 수 있는 비선형적 구조 || 다른 문서와 쉽게 연결이 되도록 한 &#39;링크&#39;의 모음으로 구성된 문서</li>
</ul>
</blockquote>
<hr>
<h2 id="roy-t-fielding이-정의하는-rest-api">Roy T. fielding이 정의하는 REST API</h2>
<h3 id="rest-아키텍쳐-스타일을-따르는-api">REST 아키텍쳐 스타일을 따르는 API</h3>
<blockquote>
<p>REST : 분산 하이퍼미디어 시스템(ex: web)을 위한 아키텍쳐 스타일 <br/>
아키텍쳐 스타일 : <strong>제약 조건의 집합</strong></p>
</blockquote>
<p>아키텍쳐 스타일에 제시된 제약 조건들을 모두 따라야 REST API라고 부를 수 있음.</p>
<h3 id="rest-아키텍쳐-스타일">REST 아키텍쳐 스타일</h3>
<ul>
<li>client-server</li>
<li>stateless</li>
<li>cache</li>
<li><strong>uniform interface</strong></li>
<li>layered system</li>
<li>code-on-demand(optional)</li>
</ul>
<p>위의 스타일 중 <strong>uniform interface</strong>를 제외한 5가지는 HTTP API로만으로 잘 지켜지지만 <strong>uniform interface</strong>에 대한 부분이 잘 지켜지지 않는다. </p>
<h2 id="uniform-interface의-제약-조건">Uniform Interface의 제약 조건</h2>
<ul>
<li>identification of resources - 리소스가 uri로 식별되야 함</li>
<li>manipulation of resources through representations - representations을 통해서 자원을 조작해야 한다.</li>
<li><strong>self-descriptive messages</strong> - 스스로 설명하는 메시지가 되어야 한다.</li>
<li><strong>hypermedia as the engin of application state (HATEOAS)</strong> - 애플리케이션의 상태가 Hyperlink를 통해서 전이되어야 한다.</li>
</ul>
<p>특히 self-descriptive messages와 HATEOAS가 안지켜진다.</p>
<hr>
<h2 id="self-descriptive-messages">Self-descriptive messages</h2>
<h3 id="self-descriptive-하지-못하는-경우---1">Self-descriptive 하지 못하는 경우 - 1</h3>
<p>API의 목적지가 어디인지 알 수 없음
<img src="https://velog.velcdn.com/images/nyong_i/post/448fbdfa-364f-4a17-b426-35b87f634a02/image.png" alt=""></p>
<h3 id="self-descriptive-하는-경우---1">Self-descriptive 하는 경우 - 1</h3>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/5ca650d2-8fa8-414c-a3bb-9bc9e61e8f12/image.png" alt=""></p>
<hr>
<h3 id="self-descriptive-하지-못하는-경우---2">Self-descriptive 하지 못하는 경우 - 2</h3>
<p>어떠한 문법으로 작성된 API 인지 알 수 없음
<img src="https://velog.velcdn.com/images/nyong_i/post/6a1d5a4d-2d16-4662-bcda-7b4d32ef6686/image.png" alt=""></p>
<p>Content-Type Header를 추가해서 작성한 문법을 알 수 있지만, &quot;op&quot;가 무엇이고, &quot;path&quot;가 무엇을 의미하는지 알 수 없다.
<img src="https://velog.velcdn.com/images/nyong_i/post/b152b309-763d-4aa2-96e8-1a57aef44ad4/image.png" alt=""></p>
<h3 id="self-descriptive-하는-경우---2">Self-descriptive 하는 경우 - 2</h3>
<p>Media-Type(json-patch+json)을 추가해 줌으로써 이 Media-Type의 명세를 찾아서 &quot;op&quot;, &quot;path&quot;가 의미하는 것을 알 수 있다.
<img src="https://velog.velcdn.com/images/nyong_i/post/3456ab23-bcfc-47da-8df9-35a3e42bc3d3/image.png" alt=""></p>
<hr>
<h2 id="hateoas">HATEOAS</h2>
<p>애플리케이션의 상태가 Hyperlink를 통해서 전이되어야 한다.</p>
<h3 id="hateoas---애플리케이션게시판">HATEOAS - 애플리케이션(게시판)</h3>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/e15b012f-9b56-4268-8a3b-94a17be942cc/image.png" alt=""></p>
<h4 id="html을-통한-hateoas">HTML을 통한 HATEOAS</h4>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/17cc5bce-4bce-4710-92b4-ecfde0e89d1d/image.png" alt=""></p>
<h2 id="왜-이걸-uniform-interface-">왜 이걸 Uniform Interface ?</h2>
<p><strong>REST를 만들게 된 계기</strong> : 어떻게 하면 기존에 HTTP로 구축되어 있던 웹들과 호환성의 문제가 생기지 않게 개발할 수 있을까?</p>
<ul>
<li>서버와 클라이언트가 각각 <strong>독립적으로 진화</strong></li>
<li>서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없음</li>
</ul>
<blockquote>
<p><strong>참고</strong> : 클라이언트와 서버가
상호간 데이터를 주고 받는 형태의 아키텍쳐(REST)를 가지고 있기 때문에 서버와 클라이언트가
독립적으로 진화할 수 있다.</p>
</blockquote>
<p>따라서 Uniform Interface가 반드시 만족되어야 REST API라고 부를 수 있음</p>
<h2 id="rest가-잘-지켜지고-있는-예">REST가 잘 지켜지고 있는 예</h2>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/fd9460b9-7229-4e9e-8e8b-00cd7d4c9695/image.jpeg" alt=""></p>
<ul>
<li>웹 페이지를 변경했다고 해서 웹 브라우저를 업데이트할 필요는 없다.</li>
<li>웹 브라우저를 업데이트했다고 웹 페이지를 변경할 필요가 없다.</li>
<li>HTTP 명세가 변경되어도 웹은 잘 동작한다.</li>
<li>HTML 명세가 변경되어도 웹은 잘 동작한다.</li>
</ul>
<hr>
<h2 id="따라서">따라서</h2>
<table>
<thead>
<tr>
<th>SOAP</th>
<th>REST</th>
</tr>
</thead>
<tbody><tr>
<td>복잡하고</td>
<td>단순하고</td>
</tr>
<tr>
<td>규칙많고</td>
<td>규칙적고</td>
</tr>
<tr>
<td>어려운</td>
<td><del><em>쉬운</em></del> -&gt; 어려운</td>
</tr>
</tbody></table>
<h3 id="그럼-api는-꼭-rest-api를-사용해야-하는가">그럼 API는 꼭 REST API를 사용해야 하는가?</h3>
<blockquote>
<p>Roy T. Fielding : 시스템 전체를 통제할 수 있거나, 
진화에 관심이 없다면 REST에 대해 따지느라 시간을 낭비하지 마라</p>
</blockquote>
<h3 id="시스템-전체를-통제하는-경우">시스템 전체를 통제하는 경우</h3>
<ul>
<li>서버 개발자가 클라이언트 개발자를 마음대로 통제할 수 있는 경우</li>
<li>혼자 서버와 클라이언트를 다 만드는 경우</li>
</ul>
<h3 id="진화에-관심이-없는-경우">진화에 관심이 없는 경우</h3>
<ul>
<li>본인이 만든 서비스에 대한 평가를 잘 받아들이지 않는 경우(본인 판단)</li>
</ul>
<h3 id="우리가-만든-api는-이제-어떻게-불러야-할까">우리가 만든 API는 이제 어떻게 불러야 할까?</h3>
<ol>
<li>REST API를 구현하고 REST API라고 부른다. - 제일 좋음</li>
<li>HTTP API라고 부른다.</li>
<li><strong>REST API가 아니지만 REST API라고 부른다.</strong> - 현재 우리들의 상황</li>
</ol>
<hr>
<h2 id="html과-json의-self-descriptive와-hateoas">HTML과 JSON의 Self-descriptive와 HATEOAS</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>HTML</th>
<th>JSON</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Self-descriptive</strong></td>
<td>O</td>
<td>O</td>
</tr>
<tr>
<td><strong>HATEOAS</strong></td>
<td>X</td>
<td>X</td>
</tr>
</tbody></table>
<h2 id="self-descriptive와-hateoas가-독립적-진화에-어떻게-도움이-될까-">Self-descriptive와 HATEOAS가 독립적 진화에 어떻게 도움이 될까 ?</h2>
<h3 id="self-descriptive---확장-가능한-커뮤니케이션">Self-descriptive - 확장 가능한 커뮤니케이션</h3>
<blockquote>
<p>서버나 클라이언트가 변경되더라도 오고가는 메시지는 언제나 self-descriptive 하므로 언제나 해석이 가능하다.</p>
</blockquote>
<h3 id="hateoas---애플리케이션-상태-전이의-late-binding">HATEOAS - 애플리케이션 상태 전이의 late binding</h3>
<blockquote>
<p>어디서 어디로 전이가 가능한지 미리 결정되지 않는다. 어떤 상태로 전이가 완료되고 나서야
그 다음 전이가 일어날 수 있는 상태가 된다. -&gt; 링크는 동적으로 변경될 수 있다.</p>
</blockquote>
<hr>
<h2 id="json이-self-descriptive를-만족시키는-방법">JSON이 Self-descriptive를 만족시키는 방법</h2>
<h3 id="방법-1--media-type-등록">방법 1 : Media-type 등록</h3>
<ol>
<li><p>미디어 타입 정의 : 
<img src="https://velog.velcdn.com/images/nyong_i/post/1d9535b1-4b82-434d-8b43-69ca7968cb04/image.png" alt=""></p>
</li>
<li><p>미디어 타입 문서 작성 : &quot;op&quot;는 뭐고, &quot;path&quot;는 뭐다</p>
</li>
<li><p>IANA에 미디어 타입을 등록한다. - 이 때 만든 문서가 미디어 타입의 명세가 됨</p>
</li>
<li><p>이제 application/json-patch+json 이라는 media-type을 보는 사람은 IANA에서 명세를 보고 해석할 수 있음</p>
</li>
</ol>
<p>단점 : 매번 Media-Type을 IANA에 올려야 하기 때문에 매우 귀찮다.</p>
<h3 id="방법-2--profile">방법 2 : Profile</h3>
<ol>
<li>&quot;id&quot;가 뭐고, &quot;title&quot;이 뭔지 의미를 정의한 명세를 작성한다.</li>
<li>Link 헤더에 profile relation으로 해당 명세를 링크한다.
<img src="https://velog.velcdn.com/images/nyong_i/post/686c18c2-3d88-4b30-a154-06f5a1adff8e/image.png" alt=""></li>
</ol>
<p>단점 : 클라이언트가 Link 헤더와 profile을 이해해야하고, Content negotiation을 할 수 없다.</p>
<hr>
<h2 id="json이-hateos를-만족시키는-방법">JSON이 HATEOS를 만족시키는 방법</h2>
<h3 id="방법-1--data">방법 1 : data</h3>
<p>data에 다양한 방법으로 하이퍼링크를 표현한다.
<img src="https://velog.velcdn.com/images/nyong_i/post/1a3ba88a-ab41-4e42-ac24-fc0f8ef0982d/image.png" alt=""></p>
<p>단점 : 링크를 표현하는 방법을 직접 정의해야한다.</p>
<h3 id="방법-2--http-헤더-사용하기">방법 2 : HTTP 헤더 사용하기</h3>
<p>Location 또는 Link를 사용하여 헤더로 링크를 표현한다.
<img src="https://velog.velcdn.com/images/nyong_i/post/744da0ae-9f30-4e49-901a-a371ffc351f3/image.png" alt=""></p>
<hr>
<h2 id="정리">정리</h2>
<ul>
<li>우리가 부르는 대부분의 &quot;REST API&quot;는 REST를 따르지 않고 있다.</li>
<li>특히 REST의 제약조건인 <strong>Self-descriptive</strong>와 <strong>HATEOAS</strong>를 잘 만족시키지 못한다.</li>
<li>REST를 따르겠다면, <strong>Self-descriptivedhk</strong>와 <strong>HATEOAS</strong>를 만족시켜야 한다.<ul>
<li>Self-descriptive : <strong>custom meadia-type</strong>이나 <strong>profile link relation</strong> 등으로 만족시킬 수 있다.</li>
<li>HATEOAS : HTTP 헤더나 본문에 <strong>Link</strong>를 담아 만족시킬 수 있다.</li>
</ul>
</li>
<li>REST API를 따르지 않겠다면, 뭐라고 부를지 결정해야 한다.</li>
</ul>
<hr>
<h3 id="references">References</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=RP_f5dMoHFc">그런 REST API로 괜찮은가</a></li>
<li><a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">Architectural Styles and the Design of Network-based Software Architectures</a></li>
<li><a href="https://www.iana.org/assignments/media-types/media-types.xhtml">IANA</a></li>
<li><a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md">REST API의 가이드라인</a></li>
<li><a href="https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven">hypertext-driven</a></li>
<li><a href="https://hanamon.kr/rest-api/">https://hanamon.kr/rest-api/</a></li>
<li><a href="https://meetup.toast.com/posts/92">https://meetup.toast.com/posts/92</a></li>
<li><a href="https://ecommerce-platforms.com/ko/glossary/content-management-system-cms">CMS</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP와 UDP가 무엇일까 ..]]></title>
            <link>https://velog.io/@nyong_i/TCP%EC%99%80-UDP%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-</link>
            <guid>https://velog.io/@nyong_i/TCP%EC%99%80-UDP%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-</guid>
            <pubDate>Sun, 05 Jun 2022 04:28:31 GMT</pubDate>
            <description><![CDATA[<h2 id="tcpip-4계층">TCP/IP 4계층</h2>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/4c23d236-1512-4868-8477-47b10631c701/image.png" alt=""></p>
<p>만약 자신이 상대방에게 정보를 전달하고 싶다면 위의 TCP/IP 4계층을 통해서 정보를 전달하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/c971e938-fbc0-419b-adde-13fa732fd995/image.png" alt=""></p>
<p>자신이 다른 사람에게 메시지를 전달한다는 가정을 해보겠습니다.</p>
<ol>
<li><p>애플리케이션 계층에서 OS계층으로 정보를 넘깁니다. 메시지를 주고 받을 때는 주로 소켓 라이브러리를 사용합니다.</p>
</li>
<li><p>애플리케이션 계층에서 받은 정보를 <strong>TCP 정보로 씌웁니다.</strong></p>
</li>
<li><p>** TCP 정보 위에 또 IP 정보를 씌웁니다.** -&gt; <strong>IP 패킷이 생성</strong>됩니다.</p>
</li>
<li><p><strong>LAN으로 정보가 나갈 때 이더넷 프레임을 통해서 나갑니다.</strong> <strong>물리적인 정보가 포함</strong>됩니다.</p>
</li>
</ol>
<hr>
<h3 id="tcp와-ip-패킷이-뭘까요-">TCP와 IP 패킷이 뭘까요 ?</h3>
<p>저희는 메시지를 상대방에게 전송할 때 OS 계층에서 2단계(TCP, IP)나 정보를 씌우고 그걸 IP 패킷이라고 불렀습니다.</p>
<p>그렇다면 TCP와 IP 패킷이 뭘까요 ?</p>
<hr>
<h2 id="tcp-전송-제어-프로토콜">TCP (전송 제어 프로토콜)</h2>
<p>TCP의 정보는 출발지 포트, 목적지 포트, 전송 제어와 관련된 정보, 순서와 관련된 정보, 검증과 관련된 정보 등등이 포함됩니다.</p>
<p>따라서 이러한 TCP 정보들이 자신이 보내고자 하는 정보를 1차적으로 포장합니다.</p>
<h3 id="tcp의-3가지-특징">TCP의 3가지 특징</h3>
<p>TCP는 3가지 특징을 가지고 있습니다.</p>
<ul>
<li>연결지향 : 자신과 상대방이 연결이 되었는가 되지 않았는가</li>
<li>데이터 전달 보증 : 자신이 보낸 데이터가 잘 전달 되었는지 되지 않았는지 알 수 있음</li>
<li>순서 보장</li>
</ul>
<hr>
<h3 id="3-way-handshake---연결-지향">3 Way Handshake - 연결 지향</h3>
<p>TCP는 그 유명한 3 Way Handshake를 사용합니다. 뭐 굳이 해석하면 ...
<img src="https://velog.velcdn.com/images/nyong_i/post/18e9390b-2ce9-4f9c-ad37-c34c500fbd6c/image.png" alt=""></p>
<p>네 .. 3 방향 악수라고 불리는 이유를 알아보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/nyong_i/post/68a1a26d-1a37-4113-acd4-2677c0ee9914/image.png" alt=""></p>
<ul>
<li>SYN : 접속 요청</li>
<li>ACK : 요청 수락</li>
</ul>
<ol>
<li>우선 클라이언트인 Host A에서 서버에게 <strong>SYN</strong>이라는 <strong>요청</strong>을 보냅니다.</li>
<li>요청을 받은 서버는 <strong>요청을 수락</strong>하면서 <strong>다시 클라이언트에게 요청</strong>을 합니다.
(서버는 요청과 수락을 동시에 하는 셈)</li>
<li>다시 요청을 받은 클라이언트는 서버가 보낸 <strong>요청을 수락</strong>하게 됩니다.</li>
</ol>
<p>이렇게 3단계로 이루어진 과정을 마친 후 데이터를 전송합니다. <em>(요즘은 3번째 단계와 함께 데이터를 보내기도 한다고 합니다.)</em></p>
<hr>
<h3 id="데이터-전달-보증">데이터 전달 보증</h3>
<p>TCP 에서는 클라이언트가 서버에게 데이터를 전송하면, 서버는 다시 클라이언트에게 데이터를 잘 받았다고 response 해줍니다.</p>
<hr>
<h3 id="순서-보장">순서 보장</h3>
<p>만약 데이터를 담은 패킷을 패킷 1, 패킷 2, 패킷 3과 같은 순서로 서버에게 전송했지만, 서버에 도착한 패킷 순서가 패킷 3, 패킷 2, 패킷 1 처럼 순서가 뒤바껴서 올 때 서버는 도착한 패킷을 다 버리고 <strong>클라이언트에게 패킷을 다시 요청</strong>합니다.</p>
<hr>
<h2 id="ip-패킷">IP 패킷</h2>
<p>TCP로 감싼 정보를 IP 정보를 한 번 더 감싸게 됨으로써 IP 패킷이 생성됩니다.
IP <strong>패킷은 출발지 IP, 목적지 IP</strong> 등등 <strong>전송 데이터를 가지고 있습니다.</strong></p>
<p>그냥 IP도 아니고 IP 패킷이 무엇인지 궁금할 수 도 있는데, 그냥 이러한 <strong>정보들을 포장한 것</strong>이라고 보면 편할 것 같습니다. (ex: 택배 박스)</p>
<hr>
<h2 id="udp-user-datagram-protocol">UDP (User Datagram Protocol)</h2>
<p>사실 UDP는 TCP에 비해서 기능이 거의 없습니다. TCP가 지원하는 연결지향, 데이터 전달 보증, 순서 보장도 해주지 않습니다.</p>
<p>UDP는 IP와 거의 비슷하며, IP + Port 라고 생각하셔도 무방할 정도입니다.</p>
<h3 id="그럼-udp는-왜-써-">그럼 UDP는 왜 써 ..?</h3>
<p>UDP가 TCP보다 뛰어난 점이 딱 한가지 있습니다.</p>
<p>바로 속도가 TCP에 비해서 빠르다는 점인데 서비스가 너무 느려서 네트워크 쪽의 성능을 최적화 하고 싶다면 우선 TCP는 제외 해야합니다.</p>
<p>이미 요즘 인터넷 자체가 TCP를 채택하고 있고, 이미 구축되어 있는 것이 많기 때문에 전송 속도도 더 빠르게 하기 힘듭니다.</p>
<p>UDP는 아무 것도 없는 상태이기 때문에 UDP에 손을 대서 최적화를 해야합니다.</p>
<hr>
<p><a href="https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC">reference</a> 
<a href="https://velog.io/@haero_kim/TCP#3-way-handshake">reference</a></p>
]]></description>
        </item>
    </channel>
</rss>