<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>땡글이LAB</title>
    <link>https://circle-lab.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 2 Jul 2026 12:46:52 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>땡글이B</managingEditor>
    <image>
      <title>땡글이LAB</title>
      <url>https://tistory1.daumcdn.net/tistory/5130564/attach/3a3e6b09bab94543960ff8e2420d9acf</url>
      <link>https://circle-lab.tistory.com</link>
    </image>
    <item>
      <title>JWT 토큰 암호화 알고리즘 - HS256과 RS256</title>
      <link>https://circle-lab.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이 글에선 JWT 토큰의 전체적인 동작 방식에 대한 내용은 다루지 않고, 암호화 알고리즘에 대해서만 다루도록 하겠습니다. JWT 토큰 암호화 알고리즘 중 대표적인 HS256, RS256에 대해서만 다루겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JWT 토큰의 전체적인 동작 방식은 &lt;a href=&quot;https://circle-lab.tistory.com/44&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;'Session(세션)과 Token(토큰)의 차이는?'&lt;/a&gt; 포스팅을 참고해주시면 좋을 것 같습니다.&lt;br /&gt;위의 포스팅에서는 대칭키 암호화 방식과 비대칭키 암호화 방식에 대해 다루고, 토큰의 구조 및 전체적인 동작 방식을 다루고 있습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SHA 256 알고리즘&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;HS256, RS256 알고리즘에서 공통적으로 쓰이는 단어인 'S256' 이라는 단어는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;SHA256 알고리즘&lt;/b&gt;&lt;/span&gt;을 의미합니다. 또한, SHA256 은 데이터 무결성을 위해 사용되는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;암호화 해쉬 알고리즘(함수)&lt;/b&gt;&lt;/span&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SHA256 알고리즘은 &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;SHA(Secure Hash Algorithm)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;알고리즘&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;의 한 종류로서 256비트로 구성되며 64자리 문자열을 반환합니다.&amp;nbsp; SHA-256은 미국의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;국립표준기술연구소&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;(NIST; National Institute of Standards and Technology)에 의해 공표된 표준 해시 알고리즘인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;http://wiki.hash.kr/index.php/SHA-2&quot;&gt;SHA-2&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;계열 중 하나이며 블록체인에서 가장 많이 채택하여 사용하고 있습니다. 이름에 내포되어 있듯&lt;span&gt; 2^256&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;만큼 경우의 수를 만들 수 있습니다. 개인용 컴퓨터로 무차별 대입을 수행해 해시 충돌 사례를 찾으려고 할 때 많은 시간이 소요될 정도로 큰 숫자이므로 충돌로부터 비교적 안전하다고 평가됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해쉬 알고리즘? 많이 들어는 봤는데,,,&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Hash(해쉬) 알고리즘&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;해쉬는 임의의 크기를 가진 데이터를 고정된 데이터의 크기로 변환시키는 함수를 말합니다. 해쉬 알고리즘에는 SHA256 만 있는 것은 아니고, SHA512, SHA3 등 다양한 해쉬 알고리즘이 있습니다. 그러나 해쉬 알고리즘을 유용하게 사용하려면 총 5가지 요구조건이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;단방향(One-Way)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;해쉬 알고리즘은 복호화할 수 없습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;결정적(Deterministic)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;만약 동일한 문서를 해쉬 알고리즘에 적용하면 똑같은 해쉬값을 얻어야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;연산이 빨라야 한다(Fast Computation)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해쉬 알고리즘을 잘 사용할 수 있을 만큼 연산 속도가 빨라야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;쇄도 효과(The Avalanche Effect)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;쇄도 효과를 이미지로 나타내면, 눈사태 이미지를 예시로 들 수 있습니다. 쇄도 효과는 해쉬 알고리즘에서 매우 중요한 요건으로 쇄도 효과란 입력값이 아주 작은 변화만 있어도 해쉬값이 완전히 달라지는 것을 의미합니다. 쇄도 효과라고 불리는 이유는 알고리즘 구현 방식에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;요점은 하나의 변화가 몇 가지의 추가적인 변화를 유발하고, 그 변화가 더 많은 변화를 유발하고 이는 더 많은 변화를 유발한다는 것입니다. 즉, 눈사태와 같다고 보면 될 것 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAMbXk/btrVq1WwCXL/ehg52mvMv38j5yOvHK2R0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAMbXk/btrVq1WwCXL/ehg52mvMv38j5yOvHK2R0K/img.png&quot; data-alt=&quot;쇄도효과를 나타내주는 그림(000, 001, 010 의 해쉬값이 전혀 다름)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAMbXk/btrVq1WwCXL/ehg52mvMv38j5yOvHK2R0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAMbXk%2FbtrVq1WwCXL%2Fehg52mvMv38j5yOvHK2R0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;272&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쇄도효과를 나타내주는 그림(000, 001, 010 의 해쉬값이 전혀 다름)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;충돌 저항성(Must withstand collisions)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;해쉬값은 고정된 길이를 가집니다. 이는 매우 다양한 조합을 만들 수 있지만, 제한적인 것에는 변함이 없습니다. 예를 들어, 해쉬값이 64비트라고 한다면, 64자로 표현할 수 있는 다양한 조합이 나올 것입니다. 하지만, 디지털 데이터의 양은 너무 방대하기 때문에 64자로도 부족할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;수학에서는 이러한 원리를 &lt;b&gt;비둘기집 원리&lt;/b&gt;라고 합니다. 만약 10마리의 비둘기가 있고 집은 9개만 있다면 하나의 집에는 2마리의 비둘기가 들어가야 합니다. 즉, 슬롯의 개수가 부족하다면 충돌이 발생할 수 밖에 없습니다. 하지만 이런 일은 드물게 발생합니다. 가끔 그런 일이 벌어져도 아무런 영향이 없을 것입니다. 최종적으로 충돌 저항성 조건이 의미하는 바는 알고리즘이 인위적인 충돌에 견딜 수 있어야 한다는 뜻입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;다시 본론으로 돌아가서, SHA256 알고리즘에 대해 중요한 부분을 정리해보자면, SHA256 알고리즘은 &quot;암호화 해쉬 알고리즘&quot;에 속하고, &quot;복호화가 불가능&quot;하다는 특성을 가집니다. 이 내용을 토대로 이제 HS256, RS256에 대해 알아보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HS256 알고리즘&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;HS256 알고리즘은&amp;nbsp;&lt;/span&gt;JWT 토큰의 암호화 알고리즘으로 많이 쓰이는 암호화 알고리즘 입니다. 앞에서 HS256에서 S256은 SHA256 을 의미한다고 했습니다. H는 무엇일까요? HMAC 알고리즘을 의미합니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HMAC 알고리즘&lt;/b&gt;&lt;/span&gt;은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;대칭키 암호화 알고리즘&lt;/b&gt;&lt;/span&gt;입니다. 대칭키 암호화 알고리즘과 JWT 구조에 대해선, &lt;a href=&quot;https://circle-lab.tistory.com/44&quot;&gt;'Session(세션)과 Token(토큰)의 차이는?'&lt;/a&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;포스팅 을 참고해주시면 되겠습니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;HS256 = HMAC + SHA256&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HS256 알고리즘을 이용한 JWT의 세부 동작 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 서버에서 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Header&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Payload&lt;/b&gt;&lt;/span&gt; 는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Base64&lt;/b&gt;&lt;/span&gt;로 인코딩된다. (참고로, Base64 알고리즘은 복호화가 되는 알고리즘입니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Signature&lt;/b&gt;&lt;/span&gt; 는 &quot;Header + Payload + secret 값&quot; 을 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HS256 알고리즘&lt;/b&gt;&lt;/span&gt;으로 암호화됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자세히 말하면, HS256 ( Base64(Header) + Base64(Payload) + secret key ) 가 됩니다. 마지막에 전체적으로 Base64로 암호화하기도 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 생성된 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Header, Payload, Signature&lt;/b&gt;&lt;/span&gt; 로 JWT 토큰을 만들어 클라이언트로 보내고, 클라이언트는 로컬 스토리지에 토큰을 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4. 클라이언트는 서버에 요청이 있을 경우, 토큰과 요청 내용을 같이 보냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;5. 서버에서는 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Header&lt;/b&gt;&lt;/span&gt; 와 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Payload&lt;/b&gt;&lt;/span&gt; 를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Base64&lt;/b&gt;&lt;/span&gt; 알고리즘으로 복호화한 뒤, 서버만 알고 있는 개인키를 가지고 다시 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HS256 알고리즘&lt;/b&gt;&lt;/span&gt;을 이용해 암호화해보고, 클라이어트에서 보낸 토큰과 같은지 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;유효성 검증&lt;/b&gt;&lt;/span&gt;을 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 다르다면, 유효하지 않은 토큰!&lt;/li&gt;
&lt;li&gt;같다면, 유효한 토큰!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btrxr7/btrVjQQhkUj/wF1oj3vg6dqEUVpp0QgpxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btrxr7/btrVjQQhkUj/wF1oj3vg6dqEUVpp0QgpxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btrxr7/btrVjQQhkUj/wF1oj3vg6dqEUVpp0QgpxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtrxr7%2FbtrVjQQhkUj%2FwF1oj3vg6dqEUVpp0QgpxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;931&quot; height=&quot;509&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RS256 알고리즘&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그리고, RS256에 대해 알아보겠습니다. RS256에서 R은 RSA 를 의미합니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;RSA 알고리즘&lt;/b&gt;&lt;/span&gt;은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;비대칭키 암호화 알고리즘&lt;/b&gt;&lt;/span&gt;입니다. 비대칭키 암호화 알고리즘은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;공개키(public key)&lt;/b&gt;&lt;/span&gt;와 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;개인키(private key)&lt;/b&gt;&lt;/span&gt;를 이용해 암호화를 진행합니다. 그리고 RS256 알고리즘은 HS256 과는 달리, secret 값이 따로 필요하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;RS256 = RSA + SHA256&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RS256 알고리즘을 이용한 JWT의 세부 동작 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;1. 서버에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Header&lt;/b&gt;&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Payload&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Base64&lt;/b&gt;&lt;/span&gt;로 인코딩된다. (참고로, Base64 알고리즘은 복호화가 되는 알고리즘입니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Header&lt;/b&gt;&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Payload&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;는 서버의 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;개인키&lt;/b&gt;&lt;/span&gt;로 암호화 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Signature&lt;/b&gt;&lt;/span&gt; 를 만듭니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 그리고 토큰을 만들어 클라이언트에 보냅니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 클라이언트는 서버에 요청을 보낼 때 토큰과 요청 내용을 같이 보냅니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 서버에서는 토큰의 유효성 검증을 하기 위해서, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;공개키&lt;/b&gt;&lt;/span&gt;로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Signature&lt;/b&gt;&lt;/span&gt; 를 복호화해봅니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비대칭키 암호화 알고리즘 특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개키로 암호화한 것은 개인키로 복호화 가능! ('&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;데이터 암호화&lt;/b&gt;&lt;/span&gt;' 기능)&lt;/li&gt;
&lt;li&gt;개인키로 암호화한 것은 공개키로 복호화 가능! ('&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;전자서명&lt;/b&gt;&lt;/span&gt;' 기능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtzVl4/btrVnZFDRqY/Icjico9lGJv01OUiDdKxA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtzVl4/btrVnZFDRqY/Icjico9lGJv01OUiDdKxA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtzVl4/btrVnZFDRqY/Icjico9lGJv01OUiDdKxA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtzVl4%2FbtrVnZFDRqY%2FIcjico9lGJv01OUiDdKxA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;540&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;Reference&lt;/span&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1672841004343&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;SHA256 - 해시넷&quot; data-og-description=&quot;SHA-256은 SHA(Secure Hash Algorithm) 알고리즘의 한 종류로서 256비트로 구성되며 64자리 문자열을 반환한다. SHA-256은 미국의 국립표준기술연구소(NIST; National Institute of Standards and Technology)에 의해 공표된 &quot; data-og-host=&quot;wiki.hash.kr&quot; data-og-source-url=&quot;http://wiki.hash.kr/index.php/SHA256&quot; data-og-url=&quot;http://wiki.hash.kr/index.php/SHA256&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b8tB89/hyQ8W0i53h/kKJx6Ld7hI5bTz88RC3gPk/img.jpg?width=213&amp;amp;height=750&amp;amp;face=30_44_78_96,https://scrap.kakaocdn.net/dn/tu6IW/hyQ80VWcRS/eMfLSwt6gftlB1VpZZXpr0/img.png?width=450&amp;amp;height=210&amp;amp;face=0_0_450_210&quot;&gt;&lt;a href=&quot;http://wiki.hash.kr/index.php/SHA256&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://wiki.hash.kr/index.php/SHA256&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b8tB89/hyQ8W0i53h/kKJx6Ld7hI5bTz88RC3gPk/img.jpg?width=213&amp;amp;height=750&amp;amp;face=30_44_78_96,https://scrap.kakaocdn.net/dn/tu6IW/hyQ80VWcRS/eMfLSwt6gftlB1VpZZXpr0/img.png?width=450&amp;amp;height=210&amp;amp;face=0_0_450_210');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SHA256 - 해시넷&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SHA-256은 SHA(Secure Hash Algorithm) 알고리즘의 한 종류로서 256비트로 구성되며 64자리 문자열을 반환한다. SHA-256은 미국의 국립표준기술연구소(NIST; National Institute of Standards and Technology)에 의해 공표된&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wiki.hash.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672842137581&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;해시 - 나무위키&quot; data-og-description=&quot;이러한 특성에 힘입어 다양한 목적에 맞게 설계된 해시 함수가 존재하며 다음과 같은 다양한 분야에서 매우 유용하게 사용된다. 해시 테이블 (또는 해시 맵)해시셋(set)중복 레코드 검색유사 레&quot; data-og-host=&quot;namu.wiki&quot; data-og-source-url=&quot;https://namu.wiki/w/%ED%95%B4%EC%8B%9C&quot; data-og-url=&quot;https://namu.wiki/w/%ED%95%B4%EC%8B%9C&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/%ED%95%B4%EC%8B%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://namu.wiki/w/%ED%95%B4%EC%8B%9C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;해시 - 나무위키&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이러한 특성에 힘입어 다양한 목적에 맞게 설계된 해시 함수가 존재하며 다음과 같은 다양한 분야에서 매우 유용하게 사용된다. 해시 테이블 (또는 해시 맵)해시셋(set)중복 레코드 검색유사 레&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;namu.wiki&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672845136628&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Blockchan A-Z] SHA256 - 해시(Hash) 이해하기&quot; data-og-description=&quot;MODULE 1 - BLOCKCHAIN INTUITION 02. UNDERSTANDING SHA256 HASH&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@octo__/SHA256-%ED%95%B4%EC%8B%9CHash-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@octo__/SHA256-해시Hash-이해하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bHME9j/hyQ8VmMerD/hhwrBcyfmHm7OykKGxwaO0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://velog.io/@octo__/SHA256-%ED%95%B4%EC%8B%9CHash-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@octo__/SHA256-%ED%95%B4%EC%8B%9CHash-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bHME9j/hyQ8VmMerD/hhwrBcyfmHm7OykKGxwaO0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Blockchan A-Z] SHA256 - 해시(Hash) 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MODULE 1 - BLOCKCHAIN INTUITION 02. UNDERSTANDING SHA256 HASH&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672845294224&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;RS256 vs HS256 JWT signing algorithms&quot; data-og-description=&quot;Question: What&amp;rsquo;s the difference between RS256 and HS256 JWT signing algorithms? TLDR; RS256 and HS256 are algorithms used for signing a JWT. RS256 is an asymmetric algorithm, meaning it uses a public and private key pair. HS256 is a symmetric algorithm, &quot; data-og-host=&quot;community.auth0.com&quot; data-og-source-url=&quot;https://community.auth0.com/t/rs256-vs-hs256-jwt-signing-algorithms/58609&quot; data-og-url=&quot;https://community.auth0.com/t/rs256-vs-hs256-jwt-signing-algorithms/58609&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6yML4/hyRaxECtSF/ZREbvqnnopZmd9nHAkoUdK/img.png?width=910&amp;amp;height=1024&amp;amp;face=0_0_910_1024,https://scrap.kakaocdn.net/dn/bTgAwB/hyQ86okrzh/Zgk4ju9bC5vBdzE3KXAum0/img.png?width=910&amp;amp;height=1024&amp;amp;face=0_0_910_1024,https://scrap.kakaocdn.net/dn/cOnzco/hyQ8Wzghwu/h7nIFN5pKyuCZvhxrKeqK0/img.png?width=558&amp;amp;height=500&amp;amp;face=0_0_558_500&quot;&gt;&lt;a href=&quot;https://community.auth0.com/t/rs256-vs-hs256-jwt-signing-algorithms/58609&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://community.auth0.com/t/rs256-vs-hs256-jwt-signing-algorithms/58609&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6yML4/hyRaxECtSF/ZREbvqnnopZmd9nHAkoUdK/img.png?width=910&amp;amp;height=1024&amp;amp;face=0_0_910_1024,https://scrap.kakaocdn.net/dn/bTgAwB/hyQ86okrzh/Zgk4ju9bC5vBdzE3KXAum0/img.png?width=910&amp;amp;height=1024&amp;amp;face=0_0_910_1024,https://scrap.kakaocdn.net/dn/cOnzco/hyQ8Wzghwu/h7nIFN5pKyuCZvhxrKeqK0/img.png?width=558&amp;amp;height=500&amp;amp;face=0_0_558_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;RS256 vs HS256 JWT signing algorithms&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Question: What&amp;rsquo;s the difference between RS256 and HS256 JWT signing algorithms? TLDR; RS256 and HS256 are algorithms used for signing a JWT. RS256 is an asymmetric algorithm, meaning it uses a public and private key pair. HS256 is a symmetric algorithm,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;community.auth0.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Web</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/46</guid>
      <comments>https://circle-lab.tistory.com/46#entry46comment</comments>
      <pubDate>Thu, 5 Jan 2023 00:17:07 +0900</pubDate>
    </item>
    <item>
      <title>다중 서버환경에서의 세션 불일치 문제와 해결방법</title>
      <link>https://circle-lab.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;세션 불일치 문제&lt;/b&gt; 는 단일 서버 환경에서는 발생하지 않으므로 따로 걱정하지 않아도 된다. 하지만, 최근 웹 서비스는 대부분 수직 확장(Scale Up)이 아닌 &lt;b&gt;수평 확장(Scale Out)&lt;/b&gt;으로 서버를 확장하기 때문에 일반적으로 다중 서버 환경일 것 이다. 이런 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;다중 서버 환경에서는 세션 불일치 문제가 발생&lt;/b&gt;&lt;/span&gt;할 수 있다. 기본적으로 세션은 서버의 메모리(RAM)에 저장되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어, 우리가 서버를 수평적으로 확장하기 위해 A, B, C 총 3대의 서버를 설치했다고 가정하자. 이때, 로드 밸런서는 유저의 요청이 들어올 때 마다 A &amp;rarr; B &amp;rarr; C &amp;rarr; A &amp;hellip; 순서대로 요청을 분산한다고 가정하자. (이를 라운드로빈 방식이라고 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBeuB6/btrVgiE7TwX/vp5DTw7apWoBbqSMxNQXqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBeuB6/btrVgiE7TwX/vp5DTw7apWoBbqSMxNQXqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBeuB6/btrVgiE7TwX/vp5DTw7apWoBbqSMxNQXqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBeuB6%2FbtrVgiE7TwX%2Fvp5DTw7apWoBbqSMxNQXqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;292&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이런 환경에서 위와 같이 특정 유저의 로그인 요청이 A 서버로 전달되었다고 가정한다면, 유저의 세션 정보는 A 서버에 생성된다. 그리고 직후에 해당 유저의 글 작성 요청이 B 서버로 전달되었다고 가정해보자. B 서버에서는 해당 유저의 세션 정보가 존재하지 않아 유효하지 않은 세션으로 인식된다. 따라서 유저의 요청이 제대로 처리되지 않을 것이다. 이런 문제를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;세션 불일치(정합성) 문제&lt;/b&gt;&lt;/span&gt; 라고한다. 세션 불일치 문제를 해결하기 위한 3가지 방법을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스티키 세션 (Sticky Session)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bubz7j/btrVe5faNKt/jV3dd7KkxXJTTkBfUoWjgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bubz7j/btrVe5faNKt/jV3dd7KkxXJTTkBfUoWjgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bubz7j/btrVe5faNKt/jV3dd7KkxXJTTkBfUoWjgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbubz7j%2FbtrVe5faNKt%2FjV3dd7KkxXJTTkBfUoWjgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;283&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째로 Sticky Session 방식이다. 클라이언트의 요청이 항상 해당 클라이언트의 세션이 저장된 서버로 향하는 방식을 말한다. 세션 정보가 없는 유저가 요청을 한 경우 로드밸런서의 기본 알고리즘대로 요청을 전달한다. 이때 이 요청으로 인해 세션이 생성된 경우 해당 유저의 요청은 해당 서버로 고정된다. 요청을 특정 서버로 고정시키는 방법은 쿠키를 통해 판단하거나, 클라이언트의 IP를 확인하여 판단하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 방식은 단순하나 크게 두 가지 문제점을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째 문제점은, 특정 서버에 트래픽이 집중될 위험성을 가지고 있다. 로드밸런서는 여러 서버에 요청을 적절히 분산해서 부하가 특정 서버에 몰리지 않기 위해 사용한다. 하지만, Sticky Session 방식으로 인해 한 서버에 트래픽이 몰리면, 로드밸런싱의 원래 목적을 달성할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;두 번째 문제점은, 하나의 서버에 장애가 발생하면 해당 서버가 들고 있는 세션 정보는 모두 유실되어 해당 서버에 고정된 유저는 다시 로그인해야하는 문제점을 가지고 있다. 즉, 가용성 문제를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;세션 클러스터링 (Session Clustering)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQhdG2/btrVgiLVPPm/niflkzktQB5FA6BEm4lQ8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQhdG2/btrVgiLVPPm/niflkzktQB5FA6BEm4lQ8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQhdG2/btrVgiLVPPm/niflkzktQB5FA6BEm4lQ8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQhdG2%2FbtrVgiLVPPm%2FniflkzktQB5FA6BEm4lQ8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;382&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Session Clustering 방식은 특정 서버에서 세션이 생성될 때 다른 서버로 세션을 전파하여 복제하는 방식으로 세션 불일치 이슈를 해결한다. 이런 방식으로 Sticky Session 방식에서 발생한 한 서버에 부하가 몰리는 문제와, 가용성 문제를 해결할 수 있다. 하지만, 대규모 클러스터에서는 Session Clustering 방식도 많은 문제점을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째로 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;비효율적인 메모리 관리 문제&lt;/b&gt;&lt;/span&gt;이다. 세션을 모든 서버가 복제하여 들고있기 때문에 효율적인 메모리 관리를 할 수 없어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;두 번째로 세션을 생성할 때 마다 데이터를 전파하고 복제하는 과정에서 Sticky Session 방식에 비해 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;많은 네트워크 트래픽을 사용한다는 문제점&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째로 세션을 전파하고 복제하는 과정에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;시간차로 인하여 &lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;세&lt;/span&gt;션 불일치 이슈가 발생할 위험성&lt;/b&gt;&lt;b&gt;이 존재&lt;/b&gt;&lt;/span&gt;한다. 예를 들어 A서버에서 생성된 세션이 B서버로는 전파가 되었지만, 아직 C서버로 전파가 되지 않았을 찰나에 클라이언트가 C서버로 요청할 수도 있는 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;세션 스토리지 외부로 분리&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/848bZ/btrVcNlQnNJ/9l5ifjlZ9L1qRdz7ox0jt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/848bZ/btrVcNlQnNJ/9l5ifjlZ9L1qRdz7ox0jt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/848bZ/btrVcNlQnNJ/9l5ifjlZ9L1qRdz7ox0jt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F848bZ%2FbtrVcNlQnNJ%2F9l5ifjlZ9L1qRdz7ox0jt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;382&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;마지막 방식은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;세션 스토리지를 외부 서버로 분리하는 방식&lt;/b&gt;&lt;/span&gt;이다. 이때 외부 세션 스토리지 서버는 일반적인 Disk-Based DB (Mysql, PostgreSQL, MongoDB 등) 를 사용할수도 있지만, 입출력이 잦은 세션 특성 상 I/O 성능이 느린 데이터베이스는 사용하기 적절하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 &lt;b&gt;세션을 저장하는 저장소로는 In-Memory DB 를 사용하는 것이 일반적&lt;/b&gt;이다. Disk-Based DB는 데이터의 영속성을 위해 사용하지만, 세션 정보는 영속성을 보장할 필요가 없다. 세션은 개발자가 설정해놓은 기간이 지나면 소멸된다. 또한 세션은 데이터가 유실된다 하더라도 그 피해가 다른 유형의 데이터에 비해 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇다면 In-Memory DB 중 어떤 DBMS를 사용하는 것이 좋을까? 세션 데이터는 Key-Value 로 구성되어 있다. 따라서 세션을 저장할때는 대표적인 Key-Value DB 인 &lt;b&gt;Redis&lt;/b&gt; 와 &lt;b&gt;Memcached&lt;/b&gt; 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세션 스토리지를 분리하면 Sticky Session 의 문제인 부하 몰림 문제와 가용성 문제를 해결할 수 있다. 또한 Session Clustering 의 비효율적인 메모리 관리 문제, 네트워크 트래픽 증가 문제, 시간차로 인한 세션 불일치 문제도 모두 해결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하지만 이런 세션 스토리지 분리 방식도 단점이 존재한다. 하나뿐인 세션 스토리지에 장애가 발생하면, 모든 서버가 세션 데이터를 정상적으로 사용할 수 없게 된다. 이 문제는 세션 스토리지를 여러 개로 구성하는 방식으로 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;i&gt;Reference&lt;/i&gt;&amp;nbsp;&lt;/h2&gt;
&lt;figure id=&quot;og_1672734969716&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring] 세션 불일치 문제가 발생하는 이유와 세션 불일치를 해결하는 법&quot; data-og-description=&quot;프로젝트에서 세션 로그인 기능을 만들었습니다. 그리고 추후 저는 대용량 트레픽을 감당할 수 있는 서버를 만들 계획이 있었습니다. 대용량 트레픽을 감당할 수 있는 서버를 만들기 위해 scale u&quot; data-og-host=&quot;junghyungil.tistory.com&quot; data-og-source-url=&quot;https://junghyungil.tistory.com/163&quot; data-og-url=&quot;https://junghyungil.tistory.com/163&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfvs2L/hyQ83D7a97/262GLkMfoEvuL4m4YQqEk1/img.png?width=800&amp;amp;height=636&amp;amp;face=0_0_800_636,https://scrap.kakaocdn.net/dn/c5kDVB/hyQ8YiwRip/PqWOcgtZOTAxM0cJbfhRg1/img.png?width=800&amp;amp;height=636&amp;amp;face=0_0_800_636,https://scrap.kakaocdn.net/dn/pohTh/hyQ81zyJtz/vhF5gwvj0Fs8gEOVw1zQf0/img.jpg?width=867&amp;amp;height=435&amp;amp;face=0_0_867_435&quot;&gt;&lt;a href=&quot;https://junghyungil.tistory.com/163&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://junghyungil.tistory.com/163&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfvs2L/hyQ83D7a97/262GLkMfoEvuL4m4YQqEk1/img.png?width=800&amp;amp;height=636&amp;amp;face=0_0_800_636,https://scrap.kakaocdn.net/dn/c5kDVB/hyQ8YiwRip/PqWOcgtZOTAxM0cJbfhRg1/img.png?width=800&amp;amp;height=636&amp;amp;face=0_0_800_636,https://scrap.kakaocdn.net/dn/pohTh/hyQ81zyJtz/vhF5gwvj0Fs8gEOVw1zQf0/img.jpg?width=867&amp;amp;height=435&amp;amp;face=0_0_867_435');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Spring] 세션 불일치 문제가 발생하는 이유와 세션 불일치를 해결하는 법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트에서 세션 로그인 기능을 만들었습니다. 그리고 추후 저는 대용량 트레픽을 감당할 수 있는 서버를 만들 계획이 있었습니다. 대용량 트레픽을 감당할 수 있는 서버를 만들기 위해 scale u&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;junghyungil.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672734525967&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;다중 서버 환경에서의 세션 불일치 문제와 해결방법&quot; data-og-description=&quot;세션 불일치 문제는 단일 서버 환경에서는 발생하지 않으므로 따로 걱정하지 않아도 된다. 하지만, 최근 웹 서비스는 대부분 수직 확장(Scale Up)이 아닌 수평 확장(Scale Out)으로 서버를 확장하기 &quot; data-og-host=&quot;hudi.blog&quot; data-og-source-url=&quot;https://hudi.blog/session-consistency-issue/&quot; data-og-url=&quot;https://hudi.blog/session-consistency-issue/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/v8UsX/hyQ87zMUQb/8TEys16CJ9UsqxXnV58Onk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/wTEsw/hyQ8Y3TaYg/m2H3XOHesn5HMVBpkBvGp0/img.png?width=680&amp;amp;height=283&amp;amp;face=0_0_680_283,https://scrap.kakaocdn.net/dn/7vdZS/hyQ8ZPf83C/eZK2jcZXHiWSC9CXGEyZnk/img.png?width=477&amp;amp;height=382&amp;amp;face=0_0_477_382&quot;&gt;&lt;a href=&quot;https://hudi.blog/session-consistency-issue/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hudi.blog/session-consistency-issue/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/v8UsX/hyQ87zMUQb/8TEys16CJ9UsqxXnV58Onk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/wTEsw/hyQ8Y3TaYg/m2H3XOHesn5HMVBpkBvGp0/img.png?width=680&amp;amp;height=283&amp;amp;face=0_0_680_283,https://scrap.kakaocdn.net/dn/7vdZS/hyQ8ZPf83C/eZK2jcZXHiWSC9CXGEyZnk/img.png?width=477&amp;amp;height=382&amp;amp;face=0_0_477_382');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;다중 서버 환경에서의 세션 불일치 문제와 해결방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;세션 불일치 문제는 단일 서버 환경에서는 발생하지 않으므로 따로 걱정하지 않아도 된다. 하지만, 최근 웹 서비스는 대부분 수직 확장(Scale Up)이 아닌 수평 확장(Scale Out)으로 서버를 확장하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hudi.blog&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Web</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/45</guid>
      <comments>https://circle-lab.tistory.com/45#entry45comment</comments>
      <pubDate>Tue, 3 Jan 2023 16:43:29 +0900</pubDate>
    </item>
    <item>
      <title>Session(세션)과 Token(토큰)의 차이는?</title>
      <link>https://circle-lab.tistory.com/44</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Stateless 한 프로토콜 : HTTP&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 HTTP의 프로토콜 상태에 알아보자. HTTP 는 stateless 한 특성 때문에 각 통신의 상태는 저장되지 않는다. 하지만 서비스에서는 어떤 유저가 기능을 사용하는지 특정할 수 있어야하는데 이를 위해서 세션(Session) 혹은 토큰(Token)이 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저가 로그인을 시도할 때 서버 상에서 일치하는 유저 정보를 찾았다면 인증 확인의 표시로 세션이나 토큰을 발급해준다. 세션과 토큰의 가장 큰 차이점은 &lt;b&gt;세션은 데이터베이스 서버에 저장된다는 것&lt;/b&gt;이고, &lt;b&gt;토큰은 클라이언트 측에서만 저장된다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[ 쿠키(Cookie) + 세션(Session) ]&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키에 ID, PW와 같은 중요한 정보가 아닌, 인증을 위한 별개의 정보를 세션 저장소에 저장하고, 클라이언트는 세션을 쿠키에 담아 서버에 요청한다. 서버는 세션 저장소에 있는 세션과 일치하는지 즉, 유효한 세션인지 확인 후 적절한 응답을 보내준다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;쿠키저장소는 어디에?&lt;/b&gt;&lt;br /&gt;&amp;nbsp;쿠키는 브라우저별로 다른 위치에 저장된다. Google Chrome은 &quot;C:\Users\&amp;lt;yourusername&amp;gt;\AppData\Local\Google\Chrome\User Data\Default\Local Storage&quot; 에 저장된다고 한다. 물론 OS마다 쿠키 위치가 달라질 것이다.&lt;br /&gt;&amp;nbsp;쿠키는 모든 HTTP 요청에 들어가는 헤더로써, 서버에서 HTTP 응답 메시지에 만료기간을 기입해 저장된 쿠키가 삭제되도록 할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 동작 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/erCUkY/btrU2KJAfO3/CzZ1VAQgyxOeVMvaHT9hx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/erCUkY/btrU2KJAfO3/CzZ1VAQgyxOeVMvaHT9hx1/img.png&quot; data-alt=&quot;세션 동작 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/erCUkY/btrU2KJAfO3/CzZ1VAQgyxOeVMvaHT9hx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FerCUkY%2FbtrU2KJAfO3%2FCzZ1VAQgyxOeVMvaHT9hx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;676&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세션 동작 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 ID/PW로 서버에 로그인&lt;/li&gt;
&lt;li&gt;ID/PW로 인증 후, 사용자를 식별한 특정 유니크한 세션 ID를 만들어 마치 자물쇠처럼 서버의 세션 저장소에 저장&lt;/li&gt;
&lt;li&gt;세션 ID를 특정한 형태로 클라이언트에 다시 반환&lt;/li&gt;
&lt;li&gt;이후 사용자 인증이 필요한 정보를 요청할 때마다 세션 ID를 쿠키에 담아 서버에 함께 전달&lt;/li&gt;
&lt;li&gt;인증이 필요한 API일 경우, 서버는 세션 ID가 세션 저장소에 저장된 것인지 즉 유효한 세션인지 확인&lt;/li&gt;
&lt;li&gt;유효한 세션이라면, 인증 완료 후 적절한 응답을 보내준다. 없다면 401 에러 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 문제점&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세션 ID, Cookie 등이 탈취된다면 세션 저장소를 전부 지워 해결 가능하지만, 탈취당하지 않은 정상적인 사용자도 모두 재인증을 해야하는 상황이 발생한다.&lt;/li&gt;
&lt;li&gt;무엇보다 HTTP의 가장 큰 특성 중 하나인 stateless한 특성을 위배하게 된다. stateless 특성은 서버에서는 클라이언트의 상태를 저장하지 않아야 하지만 세션 저장소라는 곳에서 클라이언트의 상태를 저장하게 되므로 stateful 한 상태가 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 내용이 문제가 되는 이유는 확장성에 있다. 1번 서버에서 로그인한 사용자가 다른 2번 서버로 요청하게 되면 2번 서버에서는 세션이 저장되어 있지 않아 유효하지 않은 세션으로 인식된다는 것이다.&lt;/li&gt;
&lt;li&gt;이런 문제를 해결하기 위해 세션 저장소를 별도로 외부에 두는 것이 가장 일반적인 방식이다. Redis가 세션 저장소로 가장 많이 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 문제점 해결 방안&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;세션 클러스터링 (Session Clustering)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세션 클러스터링으로 서버 간 로그인 정보가 담긴 세션을 공유하는 방법이 있지만, 실제 서비스와 관련없는 인프라적인 작업으로 서버 리소스를 많이 쓰게되는 단점이 있다.&lt;/li&gt;
&lt;li&gt;전체적인 서버 규모가 크지 않다면 나쁘지 않지만, MSA로 잘게 쪼개져 수십 수백개의 서버로 이루어진다면 단점이 극명하게 나타날 것이다.&lt;/li&gt;
&lt;li&gt;세션 클러스터링에는 방법이 여러 가지다. &quot;WAS 구성&quot;, &quot;외부에 세션 서버 구축&quot;, &quot;세션 데이터그리드 구성&quot; 방법이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스티키 세션 (Sticky Session)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스케일 아웃 시 여러 서버에 세션 정보를 복사할 필요 없도록 특정 세션을 처음 처리한 서버에게 이후 같은 세션의 요청을 같은 서버가 처리하도록 하는 방식이다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;A 사용자가 A 서버에게 요청했다면, A사용자의 요청은 모두 A서버에서 처리하는 방식이다.&lt;/li&gt;
&lt;li&gt;이 방법의 문제점은 각 서버가 균일하게 요청을 처리할 수 없다는 점에 있다. 즉, 특정 서버에만 요청이 몰리는 상황이 발생할 수 있다. 부하가 균일하게 분산되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;세션 스토리지 분리&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 방식은 세션 스토리지를 외부 서버로 분리하는 방식이다. 이 때 사용되는 세션 스토리지 서버로 일반적인 Disk-Based DB (Mysql, PostgreSQL, MongoDB 등)을 사용할 수 있지만, 입출력이 잦은 세션 특성 상 I/O 성능이 느린 데이터베이스는 사용하기에 적합하지 않다.&lt;/li&gt;
&lt;li&gt;따라서 세션을 저장하는 저장소로는 In-Memory DB를 사용하는 것이 일반적이다. In-Memory DB 중 어떤 DBMS 를 사용하는 것이 좋을까? 세션 데이터는 Key-Value로 구성되어 있다. 따라서 세션을 저장할 때는 대표적인 Key-Value DB인 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Redis&lt;/b&gt;&lt;/span&gt;와 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Memcached&lt;/b&gt;&lt;/span&gt; 를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://circle-lab.tistory.com/45&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다중 서버 환경에서의 세션 불일치 문제와 해결방법에 대해서 자세히 다루는 글&lt;/a&gt;을 따로 포스팅하였다. &lt;br /&gt;&lt;a href=&quot;https://circle-lab.tistory.com/45&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 포스팅&lt;/a&gt; 을 참고하면 될 듯하다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[ 토큰(Token) - JWT ]&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;JWT 토큰 방식은 웹표준(RFC 7519)로서 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인(self-contained) 방식으로 정보를 안정성 있게 전달한다. (자가수용적이라는 의미는 JWT 안에 인증에 필요한 모든 정보를 자체적으로 지니고 있다는 의미이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 동작 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2rslJ/btrU7Jpz3Ha/rk4i36PZzCUYUIInLEmaGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2rslJ/btrU7Jpz3Ha/rk4i36PZzCUYUIInLEmaGk/img.jpg&quot; data-alt=&quot;JWT 동작 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2rslJ/btrU7Jpz3Ha/rk4i36PZzCUYUIInLEmaGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2rslJ%2FbtrU7Jpz3Ha%2Frk4i36PZzCUYUIInLEmaGk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1772&quot; height=&quot;992&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JWT 동작 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자는 클라이언트에서 ID/PW를 통해 로그인을 요청한다.&lt;/li&gt;
&lt;li&gt;유효한 ID/PW라면, Access token &amp;amp; Refresh token을 발급한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 전달 받은 토큰들은 localStorage에 저장한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 헤더에 Access token을 담아 서버에 요청한다.&lt;/li&gt;
&lt;li&gt;서버에서는 Access token을 검증하고, 응답을 클라이언트로 보낸다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access token이 유효하지 않다면 Refersh token으로 Access token을 재발급한 뒤, access token을 리턴해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. JWT 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwGdLd/btrVcNrfKTd/h5okF6nssiZN7pKk9oKICk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwGdLd/btrVcNrfKTd/h5okF6nssiZN7pKk9oKICk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwGdLd/btrVcNrfKTd/h5okF6nssiZN7pKk9oKICk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwGdLd%2FbtrVcNrfKTd%2Fh5okF6nssiZN7pKk9oKICk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;243&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bONKN9/btrVcMeNp5r/bn3S3u4V4ky1liGCLhIy1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bONKN9/btrVcMeNp5r/bn3S3u4V4ky1liGCLhIy1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bONKN9/btrVcMeNp5r/bn3S3u4V4ky1liGCLhIy1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbONKN9%2FbtrVcMeNp5r%2Fbn3S3u4V4ky1liGCLhIy1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;368&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 내용마다 구분하기 위한 . 구분자가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-1. 헤더 (Header)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더는 두 가지 정보를 가진다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;typ - 토큰의 타입(JWT)&lt;/li&gt;
&lt;li&gt;alg - 해싱 알고리즘
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Signature 를 해싱하기 위한 알고리즘 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-2. 내용 (Payload)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Payload&lt;/b&gt;에는 토큰에 담을 정보들이 존재하고, 여기에 담는 정보의 한 조각을 &lt;b&gt;클레임(claim)&lt;/b&gt; 이라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클레임은 키 값 형태로 존재한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클레임의 종류는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;u&gt;등록된(registered) 클레임, 공개(public) 클레임, 비공개(private) 클레임&lt;/u&gt;&lt;/b&gt;&lt;/span&gt;들이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;등록된(registered) 클레임&lt;/b&gt;&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;등록된 클레임들은 서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기 위하여 이름이 이미 정해진 클레임들이다. 등록된 클레임의 사용은 모두 선택적(optional)이며, 이에 포함된 클레임 이름들은 다음과 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;iss&lt;/span&gt;: 토큰 발급자 (issuer)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;sub&lt;/span&gt;: 토큰 제목 (subject)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;aud&lt;/span&gt;: 토큰 대상자 (audience)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;exp&lt;/span&gt;: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예:&amp;nbsp;1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;nbf&lt;/span&gt;: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;iat&lt;/span&gt;: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의&lt;span&gt;&amp;nbsp;&lt;/span&gt;age&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 얼마나 되었는지 판단 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;jti&lt;/span&gt;:&amp;nbsp;JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;공개(public) 클레임&lt;/b&gt;&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개 클레임들은 충돌이 방지된 (collision-resistant) 이름을 가지고 있어야 한다. 충돌을 방지하기 위해서는, 클레임 이름을 URI 형식으로 짓는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1672641221065&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;https://velopert.com/jwt_claims/is_admin&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;비공개 (private) 클레임&lt;/b&gt;&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;등록된 클레임도 아니고, 공개된 클레임도 아니다. 양측 간에(클라이언트 - 서버) 협의 하에 사용되는 클레임 이름들이다. 공개 클레임과는 달리, 이름이 중복되어 충돌이 될 수 있으니 사용할 때 유의해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1672641366109&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;username&quot;: &quot;velopert&quot;
    &quot;email&quot;: &quot;velpert@nnn.com&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;예제 Payload&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672641387803&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;iss&quot;: &quot;velopert.com&quot;,
    &quot;exp&quot;: &quot;1485270000000&quot;,
    &quot;https://velopert.com/jwt_claims/is_admin&quot;: true,
    &quot;userId&quot;: &quot;11028373727102&quot;,
    &quot;username&quot;: &quot;velopert&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;위의 예제는 2개의 등록된 클레임, 1개의 공개 클레임, 2개의 비공개 클레임으로 이루어져 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-3. 서명 (Signature)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signature 이란 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서명 생성 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;헤더와 페이로드 값을 각각 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;BASE64&lt;/span&gt;&lt;/b&gt; 로 인코딩&lt;/li&gt;
&lt;li&gt;위에서 인코딩한 값을 비밀 키를 이용해 헤더에서 정의한 알고리즘으로 해싱&lt;/li&gt;
&lt;li&gt;위에서 해싱한 값을 다시 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;BASE64&lt;/b&gt;&lt;/span&gt; 로 인코딩&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. JWT의 장점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증에 필요한 정보가 토큰에 있기에 별도의 저장소가 필요없다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만, 보안성을 높이기 위해 Refresh Token을 사용하는 경우 별도의 저장소에 저장하면서 사용하는 경우도 있긴 하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cookie와 Session 사용 시 문제점이였던 stateful 한 특성을 JWT 토큰 사용 시에는 stateless 하게 가져갈 수 있다. &lt;b&gt;즉, 서버는 클라이언트의 상태를 가질 필요가 없다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;HTTP 헤더에 넣어서 쉽게 전달 가능하다.&lt;/li&gt;
&lt;li&gt;확장성에 용이하다. MSA 환경에 적용하기 편하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. JWT의 단점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거의 모든 요청에 토큰이 포함되므로 트래픽 크기에 영향을 미칠 수 있다.&lt;/li&gt;
&lt;li&gt;토큰에 정보가 많아져 토큰의 크기가 커지면 네트워크에 부하를 줄 수 있다.&lt;/li&gt;
&lt;li&gt;페이로드는 암호화된 게 아니라 BASE64 로 인코딩된 것이므로, 중간에 토큰을 탈취하면 페이로드의 데이터를 모두 볼 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 페이로드에는 중요 정보를 담아선 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. JWT의 암호화 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;JWT 토큰 생성 시, JWT 헤더와 페이로드 정보를 인코딩하고, 둘을 합친 문자열을 비밀 키로 서명한다. 이 때 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;대칭키 암호화, 비대칭키 암호화&lt;/b&gt;&lt;/span&gt; 방식을 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mqYH4/btrU7JpE47r/zytp1r3yMnTkAXSFBSBxc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mqYH4/btrU7JpE47r/zytp1r3yMnTkAXSFBSBxc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mqYH4/btrU7JpE47r/zytp1r3yMnTkAXSFBSBxc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmqYH4%2FbtrU7JpE47r%2Fzytp1r3yMnTkAXSFBSBxc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;314&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5-1. 대칭키 암호화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화, 복호화 키가 같으면 대칭키 암호화 방식이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 키를 사용해 암호화, 복호화를 수행하기 때문에 속도가 빠르다.&lt;/li&gt;
&lt;li&gt;대표적으로 HMAC 암호화 알고리즘이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HS256, HS384, HS512 ....&amp;nbsp; 가 이에 해당하고, 뒤 숫자는 secret key의 최소 바이트 크기를 의미한다.&lt;/li&gt;
&lt;li&gt;기본적으로 단방향 암호화 알고리즘인 SHA-256 과 함께 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;값에 SHA-256를 적용해서 해싱 후 private key( == secret key , 대칭키 역할)로 암호화 한다.&lt;/li&gt;
&lt;li&gt;private key를 알고있는 서버만 Signature 유효성 검증이 가능하다. 즉 JWT를 복호화 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5-2. 비대칭키 암호화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화, 복호화 키가 다르면 비대칭키 암호화 방식이라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 키를 사용해 암호화, 복호화를 수행하기 때문에 속도가 느리지만, 대칭키 암호화에 비해 안전하다.&lt;/li&gt;
&lt;li&gt;대표적으로 RSA 암호화 알고리즘이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마찬가지로, SHA-256 단방향 암호화 알고리즘과 함께 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;값에 SHA-256 을 적용해서 해싱 후 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;비밀키(private key)&lt;/span&gt;&lt;/b&gt;로 암호화한다.&lt;/li&gt;
&lt;li&gt;그리고 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;공개키(public key)&lt;/b&gt;&lt;/span&gt; 는 공개적으로 제공한다. 어떠한 서버든 이 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;공개키&lt;/b&gt;&lt;/span&gt;를 통해 JWT를 복호화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5-3. 대칭키 암호화 vs 비대칭키 암호화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대칭키 암호화 방식 같은 경우, private key를 모르는 서버는 JWT의 유효성을 검증할 수 없다. 반대로 비대칭키 암호화 방식은 private key를 몰라도 public key를 통해 복화할 수 있기 때문에 JWT의 유효성을 검증할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;span-stylecolor964b00대칭키-암호화-방식에서의-인증서버-구축하기span&quot; data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #964b00;&quot;&gt;대칭키 암호화 방식에서의 인증서버 구축하기&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름을 살펴보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 서버가 클라이언트에게 JWT를 발급&lt;/li&gt;
&lt;li&gt;클라이언트는 JWT와 함께 애플리케이션 서버에 요청&lt;/li&gt;
&lt;li&gt;애플리케이션 서버는 인증 서버의 private key를 모르므로 JWT를 검증할 수 없음&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 애플리케이션 서버에 인증서버의 private key를 넣어놓으면 되긴한다. 하지만 MSA 환경에서 수많은 애플리케이션 서버가 존재하는데, scale-out 할때마다 매번 private key를 넣어줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;span-stylecolor964b00비대칭키-암호화-방식에서의-인증서버-구축하기span&quot; data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #964b00;&quot;&gt;비대칭키 암호화 방식에서의 인증서버 구축하기&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름을 살펴보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 서버가 클라이언트에게 JWT를 발급&lt;/li&gt;
&lt;li&gt;클라이언트는 JWT와 함께 애플리케이션 서버에 요청&lt;/li&gt;
&lt;li&gt;애플리케이션 서버는 인증 서버의 public Key를 통해 JWT를 검증할 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 애플리케이션 서버에 일일히 key를 넣어줄 필요가 없다. public key가 공개되어 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;span-stylecolor964b00api-gateway가-존재한다면span&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #964b00;&quot;&gt;API Gateway가 존재한다면?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비대칭키 암호화 방식을 사용하면 매번 각 서버에서 필터나 인터셉터를 통해 JWT에 대한 검증을 수행할 것이다. 하지만 API Gateway가 존재하면 API GW에서만 검증하면 된다. API GW에서 public key를 통해 검증해도 되지만, 대칭키 방식을 사용해도 API GW에만 private key를 넣어주면 되므로 대칭키 방식의 문제점도 딱히 드러나지 않는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;간단히 결론을 내려보면 API GW가 없다면 비대칭키 암호화 방식을 사용하는게 좋고,&lt;br /&gt;API GW가 존재한다면 어떤 방식을 쓰든 상관없을 것 같다는 생각이 든다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. Refresh Token&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Refresh Token&lt;/span&gt;&lt;/b&gt;은 토큰이 탈취당할 경우를 대비해 사용되는 것이다. Access Token 만으로 공격자가 요청하는 것인지 정상적인 클라이언트가 요청하는 것인지 알 수 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp;Access Token&lt;/span&gt;&lt;/b&gt;은 언제든지 탈취될 수 있다고 가정하기 때문에 Access Token에는 중요한 정보를 담으면 안된다. 따라서 Access Token의 유효기간을 짧게 설정하고, Refresh Token의 유효기간을 길게 설정한다. 물론 Access Token의 유효기간 동안에는 공격에 노출되어 있지만, 피해를 최소화하기 위한 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6-1. 동작 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O4GcJ/btrVf82EyFr/khEwzsLVvUnMHJChyvaiSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O4GcJ/btrVf82EyFr/khEwzsLVvUnMHJChyvaiSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O4GcJ/btrVf82EyFr/khEwzsLVvUnMHJChyvaiSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO4GcJ%2FbtrVf82EyFr%2FkhEwzsLVvUnMHJChyvaiSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;383&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;489&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access Token이 탈취됐을 때 대비를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token&lt;span&gt;&amp;nbsp;&lt;/span&gt;개념을 도입했다. 그런데&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token과&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token&lt;span&gt;&amp;nbsp;&lt;/span&gt;모두 클라이언트에 저장되면 같이 탈취되는거 아닌가? 라는 생각이든다.&lt;/li&gt;
&lt;li&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Access Token&lt;/span&gt;&lt;/b&gt;을 &lt;b&gt;로컬 스토리지&lt;/b&gt; 또는 세션 스토리지에 저장하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Refresh Token&lt;/span&gt;&lt;/b&gt;은 쿠키에 저장하고 &lt;b&gt;보안 옵션들(HTTP Only, Secure Cookies)을 활성화&lt;/b&gt; 한다.&lt;/li&gt;
&lt;li&gt;물론&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token은 서버에도 저장되있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;span-stylecolor964b00http-only-cookiesspan&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #964b00;&quot;&gt;HTTP Only Cookies&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트에서 자바스크립트로 쿠키를 조회할 수 있는데 해당 옵션을 활성화 하면 브라우저에서 쿠키에 접근할 수 없으므로 XSS와 같은 공격으로부터 안전하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;span-stylecolor964b00secure-cookiesspan&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #964b00;&quot;&gt;Secure Cookies&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 프로토콜은 언제든지 패킷을 중간에 가로챌 수 있다. 그래서 보안개념을 추가한 HTTPS 프로토콜을 사용하여 데이터를 암호화해 통신한다.&lt;/li&gt;
&lt;li&gt;문제는 HTTPS 로 전송되어야 할 정보가 휴먼에러로 인해 HTTP 로 전송될 때 가 있다.&lt;/li&gt;
&lt;li&gt;그래서 HTTPS 프로토콜이 아닌 경우에는 쿠키를 전송하지 않도록 설정하는 옵션이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;span-stylecolord9730d5-3-refresh-token만-탈취되면span&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6-2. Refresh Token만 탈취되면?&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자는 탈취한&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Refresh Token &lt;/span&gt;&lt;/b&gt;으로 계속 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Access Token&lt;/b&gt;&lt;/span&gt;을 생성해서 정상적인 사용자처럼 서버에 계속 요청할 수 있다.&lt;/li&gt;
&lt;li&gt;이를 대비해서 서버에서 추가 검증 로직으로 방어해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB에 사용자와&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token&lt;span&gt;&amp;nbsp;&lt;/span&gt;들을 매핑하여 저장한다.&lt;/li&gt;
&lt;li&gt;정상적인 유저의&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token이 만료된 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Access Token&lt;/span&gt;&lt;/b&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Refresh Token&lt;/span&gt;&lt;/b&gt;을 서버로 보내서 새&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Access Token&lt;/span&gt;&lt;/b&gt;을 요청한다 &amp;rarr; 서버에서는 DB에 저장된Access Token,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token쌍과 클라이언트에서 보낸 토큰 쌍들을 비교한다 &amp;rarr; 일치하면 새&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token토큰을 발급해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공격자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Toekn을 탈취한 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자가 탈취한&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token으로 새&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성 요청 &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token이 없이 요청하면 공격으로 간주 &amp;rarr; 서버에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;Access Token&lt;span&gt;&amp;nbsp;&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Refresh Token&lt;span&gt;&amp;nbsp;&lt;/span&gt;폐기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. JWT 와 Session 방식 비교&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7-1. 사이즈&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8q3Ki/btrVcntT0E0/Y0wAAskpyU8e7Xn5Js6Kc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8q3Ki/btrVcntT0E0/Y0wAAskpyU8e7Xn5Js6Kc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8q3Ki/btrVcntT0E0/Y0wAAskpyU8e7Xn5Js6Kc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8q3Ki%2FbtrVcntT0E0%2FY0wAAskpyU8e7Xn5Js6Kc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;221&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세션의 경우, Cookie 헤더에 세션 ID만 실어 보내면 되므로, 트래픽을 적게 사용한다. 하지만, JWT는 사용자 인증 정보와 토큰의 발급시각, 만료시각, 토큰의 ID 등 담겨 있는 정보가 세션 ID에 비해 비대하므로 세션 방식보다 훨씬 더 많은 네트워크 트래픽을 사용한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.okta.com/blog/2017/08/17/why-jwts-suck-as-session-tokens&quot;&gt;Why JWTs Suck as Session Tokens&lt;/a&gt;&lt;span style=&quot;color: #f8f9fa;&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;게시물에 따르면, 단순히 JWT에&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;iss,&amp;nbsp;sub,&amp;nbsp;nbf,&amp;nbsp;exp,&amp;nbsp;iat,&amp;nbsp;jti,&amp;nbsp;typ&amp;nbsp;클레임만을 실었는데 304 바이트가 나왔다고 한다. 그에 비해 세션 ID는 단 6바이트. 50배가 넘는 트래픽 비효율이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7-2. 안정성과 보안성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세션의 경우, 모든 인증 정보를 서버에서 관리하기 때문에 보안 측면에서 조금 더 유리하다. 설령 세션 ID가 해커에게 탈취된다고 하더라도, 서버 측에서 해당 세션을 무효처리하면 된다.&lt;/li&gt;
&lt;li&gt;하지만, 토큰의 경우는 그렇지 않다. 토큰은 서버가 트래킹하지 않고, 클라이언트가 모든 인증정보를 가지고 있다. 따라서 토큰이 한 번 해커에게 탈취당하면 세션과 비교했을 때 조금 복잡한 방식(위의 내용 참고)으로 해킹을 막아야한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;또한 JWT 특성 상 토큰에 실린 Payload가 별도로 암호화 되어있지 않으므로, 누구나 내용을 확인할 수 있다. 따라서 Payload에는 민감한 데이터를 실을 수 없다. 즉, Payload에 실을 수 있는 데이터가 제한된다.&lt;/li&gt;
&lt;li&gt;하지만 세션과 같은 경우에는 모든 데이터가 서버에 저장되기 때문에 아무나 함부로 열람할 수 없기에 저장할 수 있는 데이터에 제한이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7-3. 확장성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;그럼에도 불구하고 최근 모던 웹 어플리케이션이 토큰 기반 인증을 사용하는 이유가 바로 이 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;확장성&lt;/b&gt;&lt;/span&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;일반적으로 웹 어플리케이션의 서버 확장 방식은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;수평 확장&lt;/b&gt;&lt;/span&gt;을 사용한다. 즉, 한 대가 아닌 여러 대의 서버가 요청을 처리하게 된다. 이때 별도의 작업을 해주지 않는다면, 세션 기반 인증 방식은 세션 불일치 문제를 겪게 된다. 이를 해결하기 위해서 Sticky Session, Session Clustering, 세션 스토리지 외부 분리 등의 작업을 해주어야한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;하지만, 토큰 기반 인증 방식의 경우 서버가 직접 인증 방식을 저장하지 않고, 클라이언트가 저장하는 방식을 취하기 때문에 이런 세션 불일치 문제로부터 자유롭다. 이런 특징으로 토큰 기반 인증 방식은 HTTP의 비상태성(Stateless)를 그대로 활용할 수 있고, 따라서 높은 확장성을 가질 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;수직확장과 수평확장에 대한 자세한 내용은 &lt;a href=&quot;https://hudi.blog/scale-up-vs-scale-out/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;수직 확장(Scale up) vs 수평 확장 (Scale out) 포스팅&lt;/a&gt;을,&lt;br /&gt;다중 서버 환경에서의 세션 불일치 문제는 &lt;a href=&quot;https://circle-lab.tistory.com/45&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다중 서버 환경에서의 세션 불일치 문제와 해결방법 포스팅&lt;/a&gt;을 참고하자.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7-4. 서버의 부담&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장성과 어느정도 이어지는 내용이다. 세션 기반 인증 방식은 서비스가 세션 데이터를 직접 저장하고, 관리한다. 따라서 세션 데이터의 양이 많아지면 많아질수록 서버의 부담이 증가할 것 이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;하지만 토큰 기반 인증 방식은 서버 대신, 클라이언트가 인증 데이터를 직접 가지고 있다. 따라서 유저의 수가 얼마나 되던 서버의 부담이 증가하지 않는다. 따라서 서버의 부담 측면에서는 세션 기반 인증 방식보다는 토큰 기반 인증 방식이 조금 더 유리함을 알 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7-5. Session 과 Token 의 차이점 정리&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stateful과 stateless 하다는 측면에서 차이점이 존재한다. 이는 토큰이 탈취됐을 때, 서버에서 능동적으로 이에 대응하여 토큰을 폐기처리할 수 있냐 없느냐에 따라 직결된다.&lt;/li&gt;
&lt;li&gt;Session 방식은 로그인한 유저의 세션 개수를 제한할 수 있다는 점에서도 차이가 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;JWT의 등장 배경을 살펴보면, 보안이 뛰어나서가 아니라, 마이크로 서비스 아키텍처(MSA)가 도입되면서 주목 받기 시작한 방식이다.&lt;/li&gt;
&lt;li&gt;아래 사진처럼 수천 수만가지의 서버 to 서버 통신이 이루어지는 아키텍처에서 중앙화된 사용자 식별 저장소를 통해 각 API 요청을 인증처리 해야한다면.. 인증 서버만 수백대가 필요할 것이다. 그렇다고 아무리 내부 서버 끼리의 통신이라고 인증을 제외할 순 없으니 JWT를 통해 인증을 진행하는 것이다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;추가로 앱과 웹을 모두 서비스하는 서버인 경우 웹에서는 Session을 이용하고 앱에서는 토큰을 이용하는 방식으로 별개의 인증방식을 가져가는게 아니라, 두 환경 모두 토큰을 기반으로 인증하여 환경에 구애받지 않고 동일한 API를 이용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;829&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqPpTv/btrVamnAnfL/MiGhJA0F4G72mKu7kJivGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqPpTv/btrVamnAnfL/MiGhJA0F4G72mKu7kJivGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqPpTv/btrVamnAnfL/MiGhJA0F4G72mKu7kJivGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqPpTv%2FbtrVamnAnfL%2FMiGhJA0F4G72mKu7kJivGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;829&quot; height=&quot;440&quot; data-origin-width=&quot;829&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;span-stylecolor0b6e997-마무리-간단-정리span&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8. 마무리 간단 정리&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 id=&quot;span-stylecolor964b00쿠키span&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;쿠키 (Cookie)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적은 용량과 보안문제점 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;span-stylecolor964b00쿠키--세션span&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;쿠키 + 세션 (Cookie + Session)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠키의 문제점을 해결&lt;/li&gt;
&lt;li&gt;문제점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태를 저장해야 하므로, 즉 세션 정보를 저장하기위한 추가 저장소가 필요하고&lt;br /&gt;이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;http stateless&lt;span&gt;&amp;nbsp;&lt;/span&gt;특성을 위반한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉 로그인할때마다 세션 스토리지(추가 저장소)에 접근해서 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스케일 아웃 시 여러 서버에 세션 정보를 복사해줘야 하는 작업이 필요하다.&lt;/li&gt;
&lt;li&gt;이를 해결하기 위한 개념인 스티키 세션, 세션 클러스터링이 등장했지만&lt;br /&gt;여전히&lt;span&gt;&amp;nbsp;&lt;/span&gt;stateful&lt;span&gt;&amp;nbsp;&lt;/span&gt;하다는 문제점을 해결하지 못했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;span-stylecolor964b00jwtspan&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;JWT (토큰, Token)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트의 상태를 들고 있을 필요 없이 토큰만으로 인증처리가 가능하다. 즉&lt;span&gt;&amp;nbsp;&lt;/span&gt;stateless하다.&lt;/li&gt;
&lt;li&gt;MSA에서 중앙화된 인증방식에 비해 유리하다.&lt;/li&gt;
&lt;li&gt;그런데 보안문제로 Refresh Token을 도입하면 결국 이를 저장하기위한 별도의 저장소가 필요한건 마찬가지이다. 즉 stateless 하지 않다.&lt;/li&gt;
&lt;li&gt;하지만 세션은 로그인할 때마다 저장소에 접근하지만, JWT는 토큰이 만료되었을때만 저장소에 접근하기 때문에 접근하는 횟수 자체는 훨씬 적다.&lt;/li&gt;
&lt;li&gt;access token을 사용하는 기간 동안은 stateless 하지만, 만료되었을 때는 stateless 가 깨지게된다.&lt;/li&gt;
&lt;li&gt;MSA 환경에서 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;Reference&lt;/i&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1672640231183&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JWT란? JWT , Session, Cookie 비교&quot; data-og-description=&quot;세션기반 인증 방식과 토큰기반 인증 방식 모두 구현해 봤지만, 두 인증방식 모두 왜 쓰는지에 대해서는 모른채 구현했다. 적어도 이런 개념들이 왜 등장했고, 각 어떤 장단점이 있는지 알고 쓰&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@znftm97/JWT-Session-Cookie-%EB%B9%84%EA%B5%90-sphsi9yh#span-stylecolor0b6e995-refresh-tokenspan&quot; data-og-url=&quot;https://velog.io/@znftm97/JWT-Session-Cookie-비교-sphsi9yh&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kYCQV/hyQ8RCRf7t/lPJBnezxcUaOnQUgM3mUl0/img.png?width=693&amp;amp;height=382&amp;amp;face=0_0_693_382,https://scrap.kakaocdn.net/dn/757Gd/hyQ852aQTB/yvBgoCVimmCgBcqnMGz1s1/img.png?width=693&amp;amp;height=382&amp;amp;face=0_0_693_382&quot;&gt;&lt;a href=&quot;https://velog.io/@znftm97/JWT-Session-Cookie-%EB%B9%84%EA%B5%90-sphsi9yh#span-stylecolor0b6e995-refresh-tokenspan&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@znftm97/JWT-Session-Cookie-%EB%B9%84%EA%B5%90-sphsi9yh#span-stylecolor0b6e995-refresh-tokenspan&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kYCQV/hyQ8RCRf7t/lPJBnezxcUaOnQUgM3mUl0/img.png?width=693&amp;amp;height=382&amp;amp;face=0_0_693_382,https://scrap.kakaocdn.net/dn/757Gd/hyQ852aQTB/yvBgoCVimmCgBcqnMGz1s1/img.png?width=693&amp;amp;height=382&amp;amp;face=0_0_693_382');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JWT란? JWT , Session, Cookie 비교&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;세션기반 인증 방식과 토큰기반 인증 방식 모두 구현해 봤지만, 두 인증방식 모두 왜 쓰는지에 대해서는 모른채 구현했다. 적어도 이런 개념들이 왜 등장했고, 각 어떤 장단점이 있는지 알고 쓰&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1672731669419&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;세션 기반 인증과 토큰 기반 인증 (feat. 인증과 인가)&quot; data-og-description=&quot;인증과 인가 세션기반 인가와 토큰기반 인가에 대해 알아보기 이전에 먼저, 인증과 인가가 무엇인지 부터 알아야할 필요가 있다. 인증과 인가를 같거나 비슷한 개념이라고 생각하는 사람들이 &quot; data-og-host=&quot;hudi.blog&quot; data-og-source-url=&quot;https://hudi.blog/session-based-auth-vs-token-based-auth/&quot; data-og-url=&quot;https://hudi.blog/session-based-auth-vs-token-based-auth/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bb4ncK/hyQ85okdbK/hIEmeSxgIfbHlC86eneL1K/img.png?width=680&amp;amp;height=382&amp;amp;face=0_0_680_382&quot;&gt;&lt;a href=&quot;https://hudi.blog/session-based-auth-vs-token-based-auth/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hudi.blog/session-based-auth-vs-token-based-auth/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bb4ncK/hyQ85okdbK/hIEmeSxgIfbHlC86eneL1K/img.png?width=680&amp;amp;height=382&amp;amp;face=0_0_680_382');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;세션 기반 인증과 토큰 기반 인증 (feat. 인증과 인가)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인증과 인가 세션기반 인가와 토큰기반 인가에 대해 알아보기 이전에 먼저, 인증과 인가가 무엇인지 부터 알아야할 필요가 있다. 인증과 인가를 같거나 비슷한 개념이라고 생각하는 사람들이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hudi.blog&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672639078701&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;세션 클러스터링이란?&quot; data-og-description=&quot;세션 클러스터링이란? 세션 클러스터링이란 WAS가 2대 이상 설치가 되어있을 경우 세션을 공유하여 대체된 WAS에도 동일한 세션을 관리하는 것을 의미합니다. 예를 들어 L4 스위치가 사용자를 접&quot; data-og-host=&quot;kku-jun.tistory.com&quot; data-og-source-url=&quot;https://kku-jun.tistory.com/44&quot; data-og-url=&quot;https://kku-jun.tistory.com/44&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JaGhC/hyQ8Ul1Oxk/PHUlQ4lomp0GdTVVZreGq1/img.png?width=532&amp;amp;height=539&amp;amp;face=0_0_532_539,https://scrap.kakaocdn.net/dn/bjThGi/hyQ83XzJBe/Q822jG63PNrrFyqK2TRhwK/img.png?width=532&amp;amp;height=539&amp;amp;face=0_0_532_539,https://scrap.kakaocdn.net/dn/bitfEa/hyQ6QS5D9D/8We0z8GGBSRhuYOIvCXMF0/img.png?width=532&amp;amp;height=539&amp;amp;face=0_0_532_539&quot;&gt;&lt;a href=&quot;https://kku-jun.tistory.com/44&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kku-jun.tistory.com/44&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JaGhC/hyQ8Ul1Oxk/PHUlQ4lomp0GdTVVZreGq1/img.png?width=532&amp;amp;height=539&amp;amp;face=0_0_532_539,https://scrap.kakaocdn.net/dn/bjThGi/hyQ83XzJBe/Q822jG63PNrrFyqK2TRhwK/img.png?width=532&amp;amp;height=539&amp;amp;face=0_0_532_539,https://scrap.kakaocdn.net/dn/bitfEa/hyQ6QS5D9D/8We0z8GGBSRhuYOIvCXMF0/img.png?width=532&amp;amp;height=539&amp;amp;face=0_0_532_539');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;세션 클러스터링이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;세션 클러스터링이란? 세션 클러스터링이란 WAS가 2대 이상 설치가 되어있을 경우 세션을 공유하여 대체된 WAS에도 동일한 세션을 관리하는 것을 의미합니다. 예를 들어 L4 스위치가 사용자를 접&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kku-jun.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1672639729170&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[TIL 3/2] 쿠키와 세션 그리고 차이점&quot; data-og-description=&quot;오늘은 객체지향 설계 및 쿠키와 세션에 대해서 학습하였습니다. 객체지향 설계는 코딩위주로 진행하여 따로 게시글로 준비할 레퍼런스를 준비하지 못했네요..! 오늘은 쿠키와 세션의 정의, 그&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@sms8377/TIL-32-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; data-og-url=&quot;https://velog.io/@sms8377/TIL-32-쿠키와-세션-그리고-차이점&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgGpeD/hyQ6X5LPGq/U3QPMLKSCCvgANiKS471A1/img.png?width=860&amp;amp;height=676&amp;amp;face=0_0_860_676,https://scrap.kakaocdn.net/dn/2vlbc/hyQ8SV3Fc2/NX7Btak7mm3WXTznrhxzok/img.png?width=860&amp;amp;height=676&amp;amp;face=0_0_860_676,https://scrap.kakaocdn.net/dn/qFLMf/hyQ6WZ5eOx/ymaFlPYul1n0i5n2DPcvKk/img.png?width=860&amp;amp;height=676&amp;amp;face=0_0_860_676&quot;&gt;&lt;a href=&quot;https://velog.io/@sms8377/TIL-32-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@sms8377/TIL-32-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgGpeD/hyQ6X5LPGq/U3QPMLKSCCvgANiKS471A1/img.png?width=860&amp;amp;height=676&amp;amp;face=0_0_860_676,https://scrap.kakaocdn.net/dn/2vlbc/hyQ8SV3Fc2/NX7Btak7mm3WXTznrhxzok/img.png?width=860&amp;amp;height=676&amp;amp;face=0_0_860_676,https://scrap.kakaocdn.net/dn/qFLMf/hyQ6WZ5eOx/ymaFlPYul1n0i5n2DPcvKk/img.png?width=860&amp;amp;height=676&amp;amp;face=0_0_860_676');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[TIL 3/2] 쿠키와 세션 그리고 차이점&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 객체지향 설계 및 쿠키와 세션에 대해서 학습하였습니다. 객체지향 설계는 코딩위주로 진행하여 따로 게시글로 준비할 레퍼런스를 준비하지 못했네요..! 오늘은 쿠키와 세션의 정의, 그&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Web</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/44</guid>
      <comments>https://circle-lab.tistory.com/44#entry44comment</comments>
      <pubDate>Mon, 2 Jan 2023 23:47:59 +0900</pubDate>
    </item>
    <item>
      <title>[책 리뷰] 자바와 JUnit을 활용한 실용주의 단위 테스트</title>
      <link>https://circle-lab.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 책을 읽게 된 계기부터 말씀드리겠습니다. 최근 전 프로젝트를 직접 기획하고 개발하고 운영해보면서, 테스트코드의 필요성을 느끼게 되었습니다. 운영 서버에서의 에러가 발견돼서 해당 사항을 수정하고, 저녁에 Github Actions + Docker를 활용해 CI/CD 파이프라인을 통해 배포했습니다. 하지만, 빠른 시간 내에 개발해야 된다는 생각에 테스트 코드를 작성해두지 않고 기존 기능들에 대해서는 Postman을 통해 API의 유효성을 검증해왔습니다. 하지만, 운영하는 기간이 늘어날수록 매번 Postman을 이용해 테스트하는 것이 비효율적이라 느꼈습니다. 그래서 저는 테스트 코드에 대해 관심을 가지고 책을 구매하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;책 읽으면서 느낀 점&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;책에서는 단위 테스트를 작성하는 방법에 대해 설명합니다. 가장 좋았던 부분은 프로젝트 상황을 예시로 들며, 그에 맞는 테스트 코드에 대해 설명해준다는 부분입니다. 책에서는 테스트 코드 작성법 및 고려해야할 부분들에 대해 정리해둔 부분이 있습니다. 만약 글로만 원칙이나 고려해야할 사항에 대해 설명했다면 잘 와닿지 않았겠지만, 코드를 예시로 들며 설명을 보니 이해가 잘 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개인적으로 읽으면서 가장 좋았던 부분은 '3장. JUnit의 단언 깊게 파기'입니다. 해당 장에서는 단언하는 방법에 대해서 설명합니다. 뿐만 아니라 테스트 코드에서 예외를 다루는 방법도 나옵니다. 3장만 읽어도 테스트 코드를 작성할 수 있을 것이라 생각이 듭니다. 물론 좋은 테스트의 FIRST 속성, 테스트 코드 작성 시 고려해야할 사항들 등등 뒤에서 다루는 내용이 더 중요합니다. 하지만, 좋은 테스트가 무엇인지, 테스트는 어떤 것을 테스트해야하는지에 대해 인지한 뒤 프로젝트에 적용해보고 실천해보면 좋은 테스트 코드에 대해 자연스럽게 배우게 될 것이라 생각합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그리고 테스트 코드를 잘 작성하기 위해선 테스트 코드를 작성하는 스킬이나 원칙에 대해 잘 아는 것 뿐만 아니라, 객체지향 설계 원칙(SOLID) 등 설계에 대한 부분도 잘 알아야 한다고 느꼈습니다. 설계가 달라지면 테스트하는 방식, 테스트 코드도 달라지게 되므로 테스트하기 쉬운 코드로 설계하는 것이 좋다는 생각이 들었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;전체적으로 느낀 건 테스트 코드 작성법에 대해 가볍게 이해할 수 있었던 책이었던 것 같습니다. 프로젝트에 직접 적용해보며 부딪혀보면서 조금 더 다듬어야 책의 내용을 진짜 본인의 것으로 체화할 수 있다고 생각이 듭니다.&lt;/p&gt;</description>
      <category>책 리뷰</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/43</guid>
      <comments>https://circle-lab.tistory.com/43#entry43comment</comments>
      <pubDate>Sun, 1 Jan 2023 19:35:29 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 탄생 배경 - SQL 에 의존적인 개발에서 벗어나자</title>
      <link>https://circle-lab.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예전 Java 개발자들은 데이터베이스에 접근해서 데이터를 가져오기 위해 직접 SQL문을 짰었습니다. 지금도 JPA 대신 JdbcTemplate 을 사용해보면, 얼마나 소모적인 작업인지 알 수 있습니다. 그래서 이 때에 많은 개발자들은 테이블을 객체로 매핑시켜주는 라이브러리 혹은 프레임워크를 개발하기 위해 많은 노력을 들였다고 합니다. 그럼 저희가 그 때 당시의 개발자라고 생각해보면서 먼저 객체와 테이블 간의 차이에 대해 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[ 객체와 관계형 데이터베이스 간의 차이 ]&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;두 가지가 다르다는 것을 확인시켜주는 중요한 2가지 개념이 존재합니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;상속&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째로는 '상속' 개념입니다. 객체에는 상속을 할 수 있지만, 관계형 데이터베이스 테이블(이하 '테이블'이라고 칭함)에는 슈퍼타입 서브타입이 존재한다. 하지만 테이블로 데이터를 삽입하기 위해선, SQL 두 개를 작성해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rzxEg/btrP3zyJVgD/eoYAkrKFE8tnYK6q5wQUok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rzxEg/btrP3zyJVgD/eoYAkrKFE8tnYK6q5wQUok/img.png&quot; data-alt=&quot;Table의 슈퍼타입 서브타입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rzxEg/btrP3zyJVgD/eoYAkrKFE8tnYK6q5wQUok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrzxEg%2FbtrP3zyJVgD%2FeoYAkrKFE8tnYK6q5wQUok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;231&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table의 슈퍼타입 서브타입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, Album 테이블에 데이터를 넣기 위해선 Item 테이블에 먼저 튜플을 삽입하고 그 이후, Album에 튜플을 삽입해야 한다. 그뿐만이 아니라, 테이블로 조회할 때에도 문제가 된다. 각각의 테이블에 맞는 조인 SQL을 작성해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하지만, 만약 객체로 Album을 삽입 / 조회한다면? 매우 간편해진다! 뿐만 아니라, 부모 타입 객체로 '&lt;b&gt;다형성&lt;/b&gt;'도 활용할 수 있다는 큰 장점이 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;연관관계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;연관관계를 가질 때, 객체는 참조를 사용하고, 테이블은 외래 키(FK : Foreign key)를 사용한다. 객체에 대해 먼저 살펴보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cL1X6d/btrP4tq2PTP/l3vMeBPDS0xZBcomKiXk8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cL1X6d/btrP4tq2PTP/l3vMeBPDS0xZBcomKiXk8K/img.png&quot; data-alt=&quot;객체의 연관관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cL1X6d/btrP4tq2PTP/l3vMeBPDS0xZBcomKiXk8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcL1X6d%2FbtrP4tq2PTP%2Fl3vMeBPDS0xZBcomKiXk8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;493&quot; height=&quot;141&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;객체의 연관관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;객체의 경우, Member에서 Team을 조회할 수 있지만, Team에서 Member를 조회할 순 없다. 하지만, 테이블에서는 외래 키와 PK 를 사용해서 Member에서도 Team을 조회할 수 있고, Team에서도 Member를 조회할 수 있게 된다. 즉, 테이블은 양방향으로 조회할 수 있지만 객체에서는 단방향으로만 조회를 할 수 있다. 그래서 객체로 연관관계 설정을 해주기 위해선, Member 객체를 만들고, 해당 member가 속할 Team 객체를 만들어주기 까지 해야한다. 2가지 테이블만 사용되었는데 프로세스가 조금 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;객체 그래프 탐색&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;객체는 그래프처럼 탐색할 수 있어야 한다. 즉, member.getOrder().getDelivery() 나 member.getTeam() 함수 모두 제대로 실행이 되어야 한다. 하지만, SQL의 경우에는 실행 시점에서의 SQL문에 의해 조회범위를 정하게 되므로, member 테이블과 team 테이블을 조인해서 조회했다면 Order, Delivery에 대한 정보는 조회할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;결론&lt;/i&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇기에 이전의 Java 개발자들은 객체 형태로 관계형 데이터베이스 테이블을 다룰 수 있는 프레임워크를 만들게 됩니다. 즉, 관계형 데이터베이스는 관계형 데이터베이스에 맞게 설계하고 객체는 객체답게 설계를 해도 중간에서 매핑해주는 작업을 ORM 프레임워크가 해주는 것입니다. Java 에서 대표적인 ORM 기술로 JPA 가 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 뿐만이 아니라, 대중적인 언어들에서는 대부분 ORM 기술이 존재합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MjmsY/btrP3MEAUmG/JDPJGNKhA67plMUnt0GbI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MjmsY/btrP3MEAUmG/JDPJGNKhA67plMUnt0GbI1/img.png&quot; data-alt=&quot;JPA 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MjmsY/btrP3MEAUmG/JDPJGNKhA67plMUnt0GbI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMjmsY%2FbtrP3MEAUmG%2FJDPJGNKhA67plMUnt0GbI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;281&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JPA 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Framework &amp;amp; Library/Spring &amp;amp; SpringBoot</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/42</guid>
      <comments>https://circle-lab.tistory.com/42#entry42comment</comments>
      <pubDate>Mon, 31 Oct 2022 19:58:47 +0900</pubDate>
    </item>
    <item>
      <title>[도커] 스웜 모드(Swarm mode) - 서비스 롤링 업데이트</title>
      <link>https://circle-lab.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;스웜 모드는 자체적으로 롤링 업데이트를 지원하며, 매우 간단하게 사용할 수 있다. 우선 롤링 업데이트를 테스트하기 위한 서비스를 생성한다. 이 서비스는 앞에서 생성한 서비스와 유사하지만 이번에는 컨테이너 생성에 사용될 이미지를 nginx:1.10 으로 설정했다.&lt;/p&gt;
&lt;pre id=&quot;code_1661860936169&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker service create --name myweb2 \
--replicas 3 \
nginx:1.10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhMuCS/btrKYFjJjrA/WDrl8QKyEVaI1VbdKHhywK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhMuCS/btrKYFjJjrA/WDrl8QKyEVaI1VbdKHhywK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhMuCS/btrKYFjJjrA/WDrl8QKyEVaI1VbdKHhywK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhMuCS%2FbtrKYFjJjrA%2FWDrl8QKyEVaI1VbdKHhywK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1522&quot; height=&quot;176&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스가 정상적으로 생성되면, docker service update 명령어로 서비스의 이미지를 업데이트할 수 있다. docker service update 명령어를 사용하면 생성된 서비스의 각종 설정을 변경할 수 있다. 이미지를 업데이트하려면 update 명령어의 --image 옵션을 설정하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;다음 명령은 myweb2 서비스의 이미지를 nginx:1.11로 업데이트한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661861054047&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker service update \
--image nginx:1.11 \
myweb2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvqpJd/btrKVxftdxB/EgsMpkNvgrxKXiRFxE46dK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvqpJd/btrKVxftdxB/EgsMpkNvgrxKXiRFxE46dK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvqpJd/btrKVxftdxB/EgsMpkNvgrxKXiRFxE46dK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvqpJd%2FbtrKVxftdxB%2FEgsMpkNvgrxKXiRFxE46dK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1612&quot; height=&quot;174&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1986&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LOqxe/btrKY3dKuRw/EbrIrEktaD3EkYP5V0D8s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LOqxe/btrKY3dKuRw/EbrIrEktaD3EkYP5V0D8s0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LOqxe/btrKY3dKuRw/EbrIrEktaD3EkYP5V0D8s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLOqxe%2FbtrKY3dKuRw%2FEbrIrEktaD3EkYP5V0D8s0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1986&quot; height=&quot;280&quot; data-origin-width=&quot;1986&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker service ps 명령어에서 &lt;b&gt;NAME 항목에 \_ 가 붙어있는 컨테이너&lt;/b&gt;는 어떠한 이유로든 동작을 멈춘 컨테이너로서, &lt;b&gt;서비스에서의 컨테이너 변경 기록&lt;/b&gt;을 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스를 생성할 때 롤링 업데이트의 주기, 업데이트를 동시에 진행할 컨테이너의 개수, 업데이트에 실패했을 때 어떻게 할 것인지를 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;아래의 예시는 각 컨테이너 레플리카를 10초 단위로 업데이트하며 업데이트 작업을 한 번에 2개의 컨테이너에 수행한다는 것을 의미한다. &lt;u&gt;&lt;i&gt;이를 설정하지 않으면 주기 없이 차례대로 컨테이너를 한 개씩 업데이트&lt;/i&gt;&lt;/u&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661861394417&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 롤링 업데이트 설정하면서 서비스 생성
$ docker service create \
--replicas 4 \
--name myweb3 \
--update-delay 10s \
--update-parallelism 2 \
nginx:1.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;위와 같은 서비스의 롤링 업데이트 설정은 docker service inspect 또는 docker inspect --type service 명령어로 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아래의 명령어는 롤링 업데이트 설정 뿐만 아니라, 서비스 자체의 정보도 출력한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661861564516&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 서비스의 정보와 롤링 업데이트 설정을 조회 
$ docker service inspect --pretty myweb3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG6BTh/btrKZvHDnuL/DzloXCK8Dv1dYtMvwYEwGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG6BTh/btrKZvHDnuL/DzloXCK8Dv1dYtMvwYEwGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG6BTh/btrKZvHDnuL/DzloXCK8Dv1dYtMvwYEwGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG6BTh%2FbtrKZvHDnuL%2FDzloXCK8Dv1dYtMvwYEwGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1776&quot; height=&quot;876&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;876&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;위의 출력 결과 중 'On failure' 항목에서 'pause'라고 표시되어 있는데, 이는 업데이트 도중 오류가 발생하면 롤링 업데이트를 중지하는 것을 의미한다. 업데이트 실패에 대해 아무런 설정을 하지 않으면 On failure 항목은 pause로 설정되지만, &lt;b&gt;서비스를 생성할 때 --update-failure-action 인자의 값을 continue로 지정해&lt;/b&gt; &lt;b&gt;업데이트 중 오류가 발생해도 계속 롤링 업데이트를 진행하게 할 수 있다&lt;/b&gt;.&amp;nbsp; &lt;b&gt;&amp;rarr;&amp;nbsp; 아래 스크립트 참고&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661861877748&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 업데이트 도중 오류 발생해도 계속 롤링 업데이트할 수 있도록 설정
$ docker service create --name myweb4 \
--replicas 4 \
--update-failure-action continue \
nginx:1.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;이러한 롤링 업데이트 옵션은 기본적으로 서비스 자체에 설정돼있지만, docker service update 명령어의 옵션 값을 다르게 설정함으로써 변경할 수 있다. 서비스 롤링 업데이트 후, 서비스를 롤링 업데이트 전으로 되돌리는 롤백(rollback) 또한 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1661862039438&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 롤링 업데이트 이전으로 되돌아감.
// (myweb2 는 이전에 롤링 업데이트가 있었던 서비스이다.)
$ docker service rollback myweb2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Devops/도커, 쿠버네티스</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/40</guid>
      <comments>https://circle-lab.tistory.com/40#entry40comment</comments>
      <pubDate>Tue, 30 Aug 2022 21:21:47 +0900</pubDate>
    </item>
    <item>
      <title>[도커] 스웜 모드(Swarm mode) - 서비스(create, replicated/global)</title>
      <link>https://circle-lab.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;이전 포스팅에서 계속 사용해온 도커 명령어의 제어 단위는 컨테이너였다. docker run 명령어는 컨테이너를 생성하고, docker rm 명령어는 컨테이너를 삭제했던 것처럼 도커 클라이언트에서 사용하는 명령어가 제어하는 것은 컨테이너이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;하지만, 스웜 모드에서 제어하는 단위는 컨테이너가 아닌 &lt;b&gt;서비스(Service)&lt;/b&gt;이다!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스(Service)는 같은 이미지에서 생성된 컨테이너의 집합이며, 서비스를 제어하면 해당 서비스 내의 컨테이너에 같은 명령이 수행된다. 서비스 내에 컨테이너는 1개 이상 존재할 수 있으며, 컨테이너들은 각 워커 노드와 매니저 노드에 할당된다. 이러한 컨테이너들은 &lt;b&gt;'태스크(Task)&lt;/b&gt;'라고 한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biZydW/btrKYFXLJBC/6k1ms85Mir4td4hRYOhiw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biZydW/btrKYFXLJBC/6k1ms85Mir4td4hRYOhiw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biZydW/btrKYFXLJBC/6k1ms85Mir4td4hRYOhiw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiZydW%2FbtrKYFXLJBC%2F6k1ms85Mir4td4hRYOhiw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;213&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;스웜 스케줄러(Scheduler)는 서비스의 정의에 따라 컨테이너를 할당할 적합한 노드를 선정하고, 해당 노드에 컨테이너를 분산해서 할당한다. 위의 그림은 각 노드에 컨테이너가 하나씩 할당된 경우를 보여주지만 반드시 각 노드에 하나씩 할당되지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이처럼 함께 생성된 컨테이너를 레플리카(replica)라고 하며, 서비스에 설정된 레플리카의 수만큼의 컨테이너가 스웜 클러스터 내에 존재해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;스웜은 서비스의 컨테이너들에 대한 상태를 계속 확인하고 있다가 서비스 내에 정의된 레플리카의 수만큼 컨테이너가 스웜 클러스터에 존재하지 않으면 새로운 컨테이너 레플리카를 생성한다. 아래의 그림에서 컨테이너가 할당된 노드가 다운되면 매니저는 사용 가능한 다른 노드에 같은 컨테이너를 생성한다. 서버가 다운되지 않더라도 서비스 내의 컨테이너 중 일부가 작동을 멈춰 정지한 상태로 있다면 이 또한 레플리카의 수를 충족하지 못하는 것으로 판단해 스웜 매니저는 새로운 컨테이너를 클러스터에 새롭게 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mBVum/btrKTPAJ7L2/fPIU0NKq1AuZB7HO7ytTA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mBVum/btrKTPAJ7L2/fPIU0NKq1AuZB7HO7ytTA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mBVum/btrKTPAJ7L2/fPIU0NKq1AuZB7HO7ytTA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmBVum%2FbtrKTPAJ7L2%2FfPIU0NKq1AuZB7HO7ytTA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;258&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스는 롤링 업데이트(Rolling update) 기능도 제공한다. 서비스 내 컨테이너들의 이미지를 일괄적으로 업데이트해야 할 때, 컨테이너들의 이미지를 순서대로 변경해 서비스 자체가 다운되는 시간 없이 컨테이너의 업데이트를 진행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;서비스 생성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스를 제어하는 도커 명령어는 전부 매니저 노드에서만 사용할 수 있다. 따라서 다음 예제는 모두 매니저 노드에서 입력한다는 전제 하에 진행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스를 사용하기 위한 명령어는 &quot;docker service&quot;로 시작한다. 서비스를 생성하려면 docker service create 명령어를 사용한다. 아래의 스크립트는 ubuntu:14.04 이미지로 서비스 내의 컨테이너를 생성하며 컨테이너가 시작할 때 실행할 명령어로 'hello world'를 출력하는 셸 명령어이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661854150133&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker service create \
ubuntu:14.04 \
/bin/sh -c &quot;while true; do echo hello world; sleep 1; done&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스 내의 컨테이너는 detached 모드로, 즉 docker run 명령어의 -d 옵션을 사용해 동작할 수 있는 이미지를 사용해야 한다. 위 스크립트 예제는 우분투 컨테이너 내에서 계속 'hello world'를 출력하기 때문에 컨테이너가 서비스로서 정상적으로 동작한다. 그러나 다음과 같이 서비스를 생성하면 컨테이너 내부를 차지하고 있는 프로세스가 없어 컨테이너가 정지될 것이고, 스웜 매니저는 서비스의 컨테이너에 장애가 생긴 것으로 판단해 컨테이너를 계속 반복해서 생성할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스를 생성했다면, 서비스의 목록을 확인해 방금 생성한 서비스가 정상적으로 구동하고 있는지 확인할 수 있다. 스웜 클러스터 내의 서비스 목록을 확인하는 명령어는 &quot;docker service ls&quot; 이다. 서비스 이름은 따로 정의하지 않았기에 서비스의 이름이 determined_raman으로 무작위로 설정된 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAdcg2/btrKVWe5GE2/yi8wnkvzbuDluCkCfGJ61K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAdcg2/btrKVWe5GE2/yi8wnkvzbuDluCkCfGJ61K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAdcg2/btrKVWe5GE2/yi8wnkvzbuDluCkCfGJ61K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAdcg2%2FbtrKVWe5GE2%2Fyi8wnkvzbuDluCkCfGJ61K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1322&quot; height=&quot;108&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스의 자세한 정보를 확인하려면 docker service ps [서비스 이름]과 같이 입력하면 된다. 이 명령어로 서비스 내의 컨테이너의 목록, 상태, 컨테이너가 할당된 노드의 위치를 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFrWfX/btrK1hhugMz/8V5nsUz4GLxAhgbIbo8i81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFrWfX/btrK1hhugMz/8V5nsUz4GLxAhgbIbo8i81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFrWfX/btrK1hhugMz/8V5nsUz4GLxAhgbIbo8i81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFrWfX%2FbtrK1hhugMz%2F8V5nsUz4GLxAhgbIbo8i81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2046&quot; height=&quot;110&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;생성된 서비스를 삭제하려면 docker service rm 명령어를 입력한다. docker rm 명령어는 컨테이너가 실행 중이면 삭제할수 없던 것과 달리, docker service rm 명령어를 사용하면 서비스의 상태에 관계없이 서비스의 컨테이너를 바로 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661856496167&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 서비스 목록 조회
$ docker service ls

// 특정 서비스의 자세한 정보 조회
$ docker service ps [서비스 이름]

// 서비스 삭제
$ docker service rm [서비스 이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Nginx 웹 서버 서비스 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;아래의 스크립트 코드는 2개의 레플리카 컨테이너를 정의하고, 서비스의 이름을 myweb으로 설정하며 컨테이너의 80번 포트를 각 노드의 80번 포트로 연결하는 서비스를 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661856760396&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker service create --name myweb \
--replicas 2 \
-p 80:80 \
nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JDnZH/btrKZfSxbUj/KsXE6Mg29bRCXBaNVVeuSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JDnZH/btrKZfSxbUj/KsXE6Mg29bRCXBaNVVeuSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JDnZH/btrKZfSxbUj/KsXE6Mg29bRCXBaNVVeuSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJDnZH%2FbtrKZfSxbUj%2FKsXE6Mg29bRCXBaNVVeuSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1902&quot; height=&quot;148&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;docker service create 명령어도 docker run과 마찬가지로 도커 데몬에 이미지가 없다면 자동으로 pull하므로 시간이 조금 걸릴 수 있다. 컨테이너가 정상적으로 생성되면 스웜 클러스터 내의 노드 중 하나를 선택해 80번 포트로 접근해 Nginx 웹 서버가 구동되고 있는 것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;위 예시에서 각 nginx 컨테이너는 swarm-manager와 swarm-worker1 노드에 생성됐다. 그렇다고해서 꼭 두 노드의 IP 주소로 접근해야만 Nginx 웹 서버에 접근할 수 있는 것은 아니다. docker service create 명령어에서 -p 옵션에 80:80 을 입력함으로써 스웜 클러스터 자체에 포트를 개방했다고 생각하면 쉽게 이해할 수 있다. 스웜 클러스터 내의 어떠한 노드로 접근해도 위 서비스의 웹 서버에 접근할 수 있다. 이를 직접 확인해보기 위해 swarm-worker2 노드의 IP 주소로 접근해본다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;실제로 nginx가 구동된 서버는 manager 와 worker1 노드에서 구동되었지만, worker2 노드에서도 nginx컨테이너로 접속이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;AWS EC2 instance로 실험할 때에는, security group에서 UDP 포트를 열어줘야 앞서 말한 worker2 노드에서 nginx 컨테이너로 접근 가능하다.&amp;nbsp;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnWPIP/btrKU7HZCGq/cIVhOd2dhHNOWyFOjctXhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnWPIP/btrKU7HZCGq/cIVhOd2dhHNOWyFOjctXhK/img.png&quot; data-alt=&quot;EC2 instance (worker1 노드를 포함하는 인스턴스의 security group)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnWPIP/btrKU7HZCGq/cIVhOd2dhHNOWyFOjctXhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnWPIP%2FbtrKU7HZCGq%2FcIVhOd2dhHNOWyFOjctXhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1008&quot; height=&quot;90&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;EC2 instance (worker1 노드를 포함하는 인스턴스의 security group)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MyMMH/btrKY26UVtJ/f8RpXMsMAbg1fAH8Qik340/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MyMMH/btrKY26UVtJ/f8RpXMsMAbg1fAH8Qik340/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MyMMH/btrKY26UVtJ/f8RpXMsMAbg1fAH8Qik340/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMyMMH%2FbtrKY26UVtJ%2Ff8RpXMsMAbg1fAH8Qik340%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;236&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 여기서 서비스 내의 Nginx 컨테이너를 4개로 늘리면 어떻게 될까? docker service scale 명령어를 이용하면 레플리카셋의 수를 늘리거나 줄일 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661858065439&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker service scale myweb=4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1998&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Minv9/btrKVx0G6l5/RQ5m6Ljbtga95Fz1D3yRf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Minv9/btrKVx0G6l5/RQ5m6Ljbtga95Fz1D3yRf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Minv9/btrKVx0G6l5/RQ5m6Ljbtga95Fz1D3yRf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMinv9%2FbtrKVx0G6l5%2FRQ5m6Ljbtga95Fz1D3yRf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1998&quot; height=&quot;218&quot; data-origin-width=&quot;1998&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;출력의 NODE 항목을 보면, 이전에 manager, worker1 노드와는 달리 worker2 노드에 2개의 컨테이너가 할당된 것을 알 수 있다. 컨테이너가 각 컨테이너들이 호스트의 80번 포트에 연결된 것이 아니라 실제로는 각 노드의 80번 포트로 들어온 요청을 위 4개의 컨테이너 중 1개로 리다이렉트(redirect)하기 때문이다. 따라서 각 호스트의 어느 노드로 접근하든 4개의 컨테이너 중 1개에 접근하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;스웜 모드는 라운드 로빈(Round-Robin) 방식으로 서비스 내에 접근할 컨테이너를 결정한다. 각 노드의 트래픽이나 자원 사용량 등을 고려해 로드 밸런싱을 해야 한다면 이 방식은 적합하지 않을 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Global 서비스 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;서비스의 모드는 두 가지가 있다. 하나는 위에서 생성한 Nginx 웹서버 서비스와 같이 레플리카셋의 수를 정의해 그만큼의 같은 컨테이너를 생성하는 &lt;b&gt;복제 모드(replicated)&lt;/b&gt;로서 실제 서비스를 제공하기 위해 일반적으로 쓰이는 모드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;다른 하나는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;글로벌(global) 모드&lt;/b&gt;&lt;/span&gt;이다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;글로벌 서비스는 스웜 클러스터 내에서 사용할 수 있는 모든 노드에 컨테이너를 반드시 하나씩 생성한다&lt;/b&gt;&lt;/span&gt;. 따라서 글로벌 모드로 생성한 서비스는 레플리카셋의 수를 별도로 지정하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;글로벌 서비스는 스웜 클러스터를 모니터링하기 위한 에이전트 컨테이너 등을 생성해야 할 때 유용하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;글로벌 서비스는 다음과 같이 docker service create 명령어에 --mode global을 추가해 생성할 수 있다. --mode 옵션이 없으면 default로 복제 모드로 지정된다.&lt;/p&gt;
&lt;pre id=&quot;code_1661858523591&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker service create --name global_web \
--mode global \
nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HEHAq/btrK0M9S3XF/KBJtsErEUqZ5tsfHbtE5F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HEHAq/btrK0M9S3XF/KBJtsErEUqZ5tsfHbtE5F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HEHAq/btrK0M9S3XF/KBJtsErEUqZ5tsfHbtE5F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHEHAq%2FbtrK0M9S3XF%2FKBJtsErEUqZ5tsfHbtE5F1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2358&quot; height=&quot;180&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;확인해보면 알 수 있듯이, 각 노드에 컨테이너가 하나씩 생성되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스웜 모드의 서비스 장애 복구&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;앞에서 이야기한 것처럼, 복제 모드로 설정된 서비스의 컨테이너가 정지하거나 특정 노드가 다운되면 스웜 매니저는 새로운 컨테이너를 생성해 자동으로 이를 복구한다. 위에서 생성한 myweb 서비스 중 컨테이너 하나를 삭제해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNi7cU/btrK0F31sZu/9lTRu2h3viH04xMbnYdrmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNi7cU/btrK0F31sZu/9lTRu2h3viH04xMbnYdrmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNi7cU/btrK0F31sZu/9lTRu2h3viH04xMbnYdrmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNi7cU%2FbtrK0F31sZu%2F9lTRu2h3viH04xMbnYdrmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;72&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2282&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cufgh6/btrK1qS3bBU/Z3o5W2GYfRK9vbpKbetJn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cufgh6/btrK1qS3bBU/Z3o5W2GYfRK9vbpKbetJn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cufgh6/btrK1qS3bBU/Z3o5W2GYfRK9vbpKbetJn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcufgh6%2FbtrK1qS3bBU%2FZ3o5W2GYfRK9vbpKbetJn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2282&quot; height=&quot;248&quot; data-origin-width=&quot;2282&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;뭔가 새로운 컨테이너가 생성되었다. 추가적으로 3개의 노드 중 swarm-worker1 노드의 도커 데몬 프로세스를 종료해 임의로 노드 장애 상태를 만들어본다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661859029692&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(worker1 노드) $ service docker stop&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAW6qo/btrKUSYmd0V/asXhz1KXobVQQbtMUCjCTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAW6qo/btrKUSYmd0V/asXhz1KXobVQQbtMUCjCTK/img.png&quot; data-alt=&quot;manager 노드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAW6qo/btrKUSYmd0V/asXhz1KXobVQQbtMUCjCTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAW6qo%2FbtrKUSYmd0V%2FasXhz1KXobVQQbtMUCjCTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2146&quot; height=&quot;178&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;manager 노드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;매니저 노드에서 docker node ls 명령어를 통해, worker1 노드의 상태가 Down으로 바뀐 것을 확인할 수 있다. docker service ps 명령어로 다시 컨테이너의 목록을 확인해보면 종료(shutdown)됐으며, 이를 복구하기 위한 컨테이너가 manager 노드에 생성되었음을 알 수 있다. &lt;u&gt;&lt;b&gt;[아래 이미지 참고!]&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2366&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CipGP/btrKUTpve2b/qM9ELJkcAkmbgRrJGf0O41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CipGP/btrKUTpve2b/qM9ELJkcAkmbgRrJGf0O41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CipGP/btrKUTpve2b/qM9ELJkcAkmbgRrJGf0O41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCipGP%2FbtrKUTpve2b%2FqM9ELJkcAkmbgRrJGf0O41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2366&quot; height=&quot;280&quot; data-origin-width=&quot;2366&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;다운됐던 노드를 다시 시작해 정상적인 상태를 회복해도 장애를 복구하기 위해 다른 노드로 옮겨진 컨테이너가 해당 노드에 자동으로 할당되지는 않는다. 위 예에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;worker1 노드에서 도커 엔진을 다시 시작해 컨테이너를 실행할 수 있는 환경을 복구해도&lt;/b&gt;&lt;/span&gt; myweb.1 컨테이너가 다시 worker1 노드로 돌아가는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;재균형(rebalance) 작업이 일어나지는 않는다&lt;/b&gt;&lt;/span&gt;는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;즉, &lt;b&gt;새로운 노드를 추가하거나 다운됐던 노드를 다시 복구했을 때 서비스의 컨테이너 할당의 균형을 맞추기 위해서는&lt;/b&gt; scale 명령어를 이용해 컨테이너의 수를 줄이고 다시 늘려야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661859541471&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 복구 혹은 서비스 컨테이너 할당의 균형 맞추기 위한 프로세스
$ docker service scale myweb=1
$ docker service scale myweb=4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;References&lt;/i&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Devops/도커, 쿠버네티스</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/39</guid>
      <comments>https://circle-lab.tistory.com/39#entry39comment</comments>
      <pubDate>Tue, 30 Aug 2022 20:42:38 +0900</pubDate>
    </item>
    <item>
      <title>[도커] 스웜 모드(Swram mode) - 개념, manager 및 worker node</title>
      <link>https://circle-lab.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지금까지 알아본 도커 사용법은 대부분 하나의 호스트를 기준으로 한다. docker ps 명령어는 하나의 도커 엔진에 존재하는 컨테이너의 목록을 출력하며 create, run 명령어 또한 하나의 도커 엔진에 컨테이너를 생성한다. 그러나 실제로 도커를 운영환경에 적용하려면 이야기가 달라진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하나의 호스트 머신에서 도커 엔진을 구동하다가 CPU나 메모리, 디스크 용량과 같은 자원이 부족하면 이를 어떻게 해결할까??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 답은 &quot;매우 성능이 좋고 디스크 용량이 큰 좋은 서버를 새로 산다&quot;이다. 하지만, 자원의 확장성 측면이나 비용 측면에서도 이것은 좋은 해답은 아니다. 자원이 부족할 때마다 성능이 좋은 서버를 살 수 없을 뿐더러 높은 가격의 서버를 사고 유지하는 비용 또한 무시할 수 없기 때문에 이를 해결하기 위해 여러 가지 방법이 제안됐다. 이 중 가장 많이 사용하는 방법은 여러 대의 서버를 클러스터로 만들어 자원을 병렬로 확장시키는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckVs36/btrKLfZYqW4/XzhUfNdjdXYZgoxpK6a860/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckVs36/btrKLfZYqW4/XzhUfNdjdXYZgoxpK6a860/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckVs36/btrKLfZYqW4/XzhUfNdjdXYZgoxpK6a860/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckVs36%2FbtrKLfZYqW4%2FXzhUfNdjdXYZgoxpK6a860%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;187&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;그러나! 여러 대의 서버를 하나의 자원 풀로 만드는 것은 쉬운 작업이 아니다. 새로운 서버나 컨테이너가 추가됐을 때, 이를 발견(Service Discovery)하는 작업부터 어떤 서버에 컨테이너를 할당할 것인가에 대한 스케줄러와 로드밸런서 문제, 클러스터 내의 서버가 다운됐을 때 고가용성(Hig Availability) 을 어떻게 보장할지 등이 문제로 남아있다. 그러나 다행히도 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;이러한 문제를 해결하는 여러 솔루션&lt;/span&gt;을 오픈소스로 활용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;대표적인 것이 도커에서 공식적으로 제공하는 &lt;b&gt;&quot;도커 스웜(docker swarm)&quot;,&lt;/b&gt;&amp;nbsp;&lt;b&gt;&quot;스웜 모드(swarm mode)&quot;&lt;/b&gt; 이다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스웜 클래식과 도커 스웜 모드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;스웜 클래식과 스웜 모드는 앞서 말했듯이 여러 대의 도커 서버를 하나의 클러스터로 만들어 컨테이너를 생성하는 여러 기능을 제공한다. 다양한 전략을 세워 컨테이너를 특정 도커 서버에 할당할 수 있고 유동적으로 서버를 확장할 수도 있다. 그뿐만 아니라 스웜 클러스터에 등록된 서버의 컨테이너를 쉽게 관리할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;따라서 PaaS 와 같은 용도로 도커 서버 클러스터링을 고려하고 있다면, 가장 먼저 스웜을 사용해보는 것이 좋다. 다만, 도커 스웜 모드가 실제 운영 환경에서 많이 쓰이는 것은 아니지만 서버 클러스테어서 컨테이너를 어떻게 다루는 지에 대한 기초적인 지식을 쌓기에 적합하기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;도커 스웜에는 두 가지 종류가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;도커 버전 1.6 이후부터 사용할 수 있는 &lt;b&gt;컨테이너로서의 스웜 &amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;스웜 클래식&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;도커 버전 1.12 이후부터 사용할 수 있는 &lt;b&gt;도커 스웜 모드(Swarm mode) &lt;b&gt;&amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;스웜 모드&lt;/span&gt;&lt;/b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(두 가지 종류의 스웜을 구분하기 위해 위와 같이 부르도록 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;b&gt;스웜 클래식과 스웜 모드의 가장 큰 차이점&lt;/b&gt;은 바로 그 목적에 있다. &lt;b&gt;스웜 클래식&lt;/b&gt;은 여러 대의 도커 서버를 하나의 지점에서 사용하도록 단일 접근점을 제공한다면 &lt;b&gt;스웜 모드&lt;/b&gt;는 마이크로서비스 아키텍처의 컨테이너를 다루기 위한 클러스터링 기능에 초점을 맞추고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;스웜 클래식은 docker run, docker ps 등 일반적인 도커 명령어와 도커 API로 클러스터의 서버를 제어하고 관리할 수 있는 기능을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;b&gt;스웜 모드&lt;/b&gt;는 같은 컨테이너를 동시에 여러 개 생성해 필요에 따라 &lt;u&gt;&lt;b&gt;유동적으로 컨테이너의 수를 조절&lt;/b&gt;&lt;/u&gt;할 수 있으며, 컨테이너로의 연결을 분산하는 &lt;u&gt;&lt;b&gt;로드밸런싱 기능&lt;/b&gt;&lt;/u&gt;을 자체적으로 지원한다. 개발하는 어플리케이션의 특성에 따라 적절한 것을 선택해 사용하면 되지만, &lt;b&gt;스웜 모드가 서비스 확장성과 안정성 등 여러 측면에서 스웜 클래식보다 뛰어나기 때문에&lt;/b&gt; 일반적으로는 스웜 모드를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;스웜 클래식과 스웜 모드의 다른 차이점은 분산 코디네이터(Distributed Coordinator), 에이전트와 같은 클러스터 툴이 별도로 구동되느냐입니다. 여러 개의 도커 서버를 하나의 클러스터로 구성하려면 각종 정보를 저장하고 동기화하는 분산 코디네이터, 클러스터 내의 서버를 관리하고 제어하는 매니저, 각 서버를 제어하는 에이전트가 반드시 있어야한다. 스웜 클래식은 분산 코디네이터, 에이전트 등이 별도로 실행돼야 하지만, 스웜 모드는 클러스터링을 위한 모든 도구가 도커 엔진 자체에 내장되어 있기 때문에 더욱 쉽게 서버 클러스터를 구축할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산 코디네이터는 클러스터에 영입할 새로운 서버의 발견, 클러스터의 각종 설정 저장, 데이터 동기화 등에 주로 이용된다. etcd, zookeeper, consul 등이 대표적인 예이며, 스웜 클래식은 대부분의 분산 코디네이터를 사용할 수 있다. 스웜 모드는 분산 코디네이터를 별도로 구축하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CGmJ5/btrKQi9Hc6j/63hy24QBC6HAUZqbqbFeak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CGmJ5/btrKQi9Hc6j/63hy24QBC6HAUZqbqbFeak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CGmJ5/btrKQi9Hc6j/63hy24QBC6HAUZqbqbFeak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCGmJ5%2FbtrKQi9Hc6j%2F63hy24QBC6HAUZqbqbFeak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;306&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;쿠버네티스는 스웜 모드와 비슷하지만, 더욱 복잡하고 많은 기능을 제공하는 솔루션이다. 따라서 마이크로서비스, 컨테이너 클라우드 등에 대한 기본 개념을 이해하지 않으면 쿠버네티스를 이해하기 어려울 수 있다. 스웜 모드는 클라우드에서 마이크로서비스 아키텍처의 컨테이너가 어떻게 배포되고 사용될 수 있는지를 학습하기에 나쁘지 않은 솔루션이기 때문에 스웜 모드를 이해한다면 쿠버네티스를 이해하는데에 수월할 것입니다! :)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스웜 모드(Swarm mode)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;스웜 모드는 별도의 설치과정 없이 도커 엔진 자체에 내장돼있다. docker info 명령어를 통해 도커 엔진의 스웜 모드 클러스터 정보를 확인할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;docker info | grep Swarm&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;이전 포스팅에서 다룬 도커 엔진을 사용한 방법들은 모두 단일 도커 서버에서 사용된 것이므로 현재 스웜 모드의 상태는 비활성(inactive)으로 설정되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서버 클러스터링을 할 때는 반드시 각 서버의 시간을 NTP 등의 툴을 이용해 동기화 해야한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;도커 스웜 모드의 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;스웜 모드는 매니저 노드와 워커(Worker) 노드로 구성돼있다. 워커 노드는 실제로 컨테이너가 생성되고 관리되는 도커 서버이고 매니저 노드는 워커 노드을 관리하기 위한 도커 서버입니다. 그렇지만 매니저 노드에도 컨테이너가 생성될 수 있다. 즉, 매니저 노드는 기본적으로 워커 노드의 역할을 포함하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;매니저 노드는 1개 이상 있어야 하지만, 워커 노드는 없을 수도 있다. 이는 매니저 노드가 워커 노드의 역할도 포함하고 있어 매니저 노드만으로 스웜 클러스터를 구성할 수 있기 때문이다. 그러나 일반적으로 워커 노드와 매니저 노드를 구분해서 사용하는 것을 권장한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;매니저 노드를 1개만 사용하지만, 운영환경에서 스웜 모드로 도커 클러스터를 구성하려면 매니저 노드를 다중화하는 것을 권장한다. 이렇게 하면 매니저의 부하를 분산하고 특정 매니저 노드가 다운됐을 때 정상적으로 스웜 클러스터를 유지할 수 있기 때문이다. 그러나 매니저 수를 늘린다고 스웜 클러스터의 성능이 좋아지는 것은 아니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;스웜 모드는 매니저 노드의 절반 이상에 장애가 생겨 정상적으로 작동하지 못할 경우, 장애가 생긴 매니저 노드가 복구될 때까지 클러스터의 운영을 중단한다. 만약 매니저 노드 사이에 네트워크 파티셔닝과 같은 현상이 발생했을 경우, 짝수 개의 매니저로 구성한 클러스터는 운영이 중단될 수도 있지만 홀수 개로 구성했을 경우에는 과반수 이상이 유지되는 쿼럼(quorum) 매니저에서 운영을 계속할 수 있다. &lt;b&gt;따라서 스웜 매니저는 되도록이면 홀수 개로 구성하는 것이 권장된다고 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;먼저 docker swarm init 명령어를 입력해 매니저 역할을 할 서버에서 스웜 클러스터를 시작한다. --advertise-addr 에는 다른 도커 서버가 매니저 노드에 접근하기 위한 IP 주소를 입력한다. 즉, 매니저 노드의 IP 주소를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661755672174&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# docker swarm init --advertise-addr 192.168.0.100&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MzvMT/btrKSLjBMV3/ulc1ZNINqZ41Z8eNjBweKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MzvMT/btrKSLjBMV3/ulc1ZNINqZ41Z8eNjBweKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MzvMT/btrKSLjBMV3/ulc1ZNINqZ41Z8eNjBweKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMzvMT%2FbtrKSLjBMV3%2Fulc1ZNINqZ41Z8eNjBweKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1173&quot; height=&quot;142&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;매니저 노드에 2개 이상의 네트워크 인터페이스 카드가 있을 경우, 어느 IP 주소로 매니저에 접근해야 할지 다른 노드에 알려줄 필요가 있다. 예를 들어 ifconfig 같은 명령어를 입력했을 때, 출력된 IP 주소가 172.17.0.5와 192.168.0.100으로 두 개가 존재한다면 스웜 클러스터 내에서 사용할 IP 주소를 지정해야 한다. 두 IP 주소 중 전자가 Private IP, 후자가 Public IP 라면 후자를 --advertise-addr 에 지정해 다른 노드가 해당 노드에 접근할 수 있게 설정해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;출력결과 중 docker swarm join 명령어는 새로운 워커노드를 스웜 클러스터에 추가할 때 사용된다. --token 옵션에 사용된 토큰 값은 새로운 노드를 해당 스웜 클러스터에 추가하기 위한 비밀 키이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;nbsp;워커 노드에서는 위의 &quot;docker swarm join --token ...&quot; 명령어를 입력해서, 새로운 워커 노드를 스웜 클러스터에 추가한다. (총 2개의 워커 노드를 아래와 같이 스웜 클러스터에 추가시킨다)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7U8p4/btrKTTByqqX/nYsDRIKSfqlqD45mDs1l3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7U8p4/btrKTTByqqX/nYsDRIKSfqlqD45mDs1l3K/img.png&quot; data-alt=&quot;첫 번째 워커 노드를 스웜 클러스터에 추가시켰을 때의 터미널 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7U8p4/btrKTTByqqX/nYsDRIKSfqlqD45mDs1l3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7U8p4%2FbtrKTTByqqX%2FnYsDRIKSfqlqD45mDs1l3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1332&quot; height=&quot;60&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;첫 번째 워커 노드를 스웜 클러스터에 추가시켰을 때의 터미널 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;총 2개의 워커 노드가 스웜 클러스터에 추가되면, 매니저 노드에서 docker node ls 명령어로 확인해보면 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661758233405&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# docker node ls&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2MJpf/btrKQQMfrxL/uaJFmBXnpO72c7z77DomE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2MJpf/btrKQQMfrxL/uaJFmBXnpO72c7z77DomE1/img.png&quot; data-alt=&quot;* 표시는 매니저 노드라는 것을 의미한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2MJpf/btrKQQMfrxL/uaJFmBXnpO72c7z77DomE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2MJpf%2FbtrKQQMfrxL%2FuaJFmBXnpO72c7z77DomE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;86&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;* 표시는 매니저 노드라는 것을 의미한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;새로운 매니저 노드 추가하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;새로운 매니저 노드를 추가하려면, 매니저 노드를 위한 토큰을 사용해 docker swarm join 명령어를 사용한다. &lt;b&gt;매니저 노드를 추가하기 위한 토큰&lt;/b&gt;은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;docker swarm join-token manager&lt;/span&gt; 명령어로 확인할 수 있다. (이와 마찬가지로 워커 노드를 추가하기 위한 명령어도 있다) &lt;u&gt;&lt;b&gt;&lt;b&gt;&amp;rarr;&lt;/b&gt;&lt;/b&gt;&lt;b&gt;&lt;u&gt;&amp;nbsp;&lt;/u&gt;단, 이 작업은 매니저 노드에서만 수행할 수 있다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;토큰을 갱신하려면, swarm join 명령어에 --rotate 옵션을 추가하고 변경할 토큰의 대상을 입력하면 된다. 아래의 명령어와 같이 입력하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1661758403068&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 매니저 노드 추가하기 위한 토큰
# docker swarm join-token manager


// 워커 노드 추가하기 위한 토큰
# docker swarm join-token worker


// 토큰 갱신 (매니저 노드를 추가하는 토큰 갱신)
# docker swarm join-token --rotate manager


// 토큰 갱신 (워커 노드를 추가하는 토큰 갱신)
# docker swarm join-token --rotate worker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;추가된 워커 노드를 삭제하고 싶으면, 해당 워커 노드에서 docker swarm leave 명령어를 입력하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1661758830073&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; // (해당 워커 노드 상에서) 워커 노드 삭제 = 해당 워커 노드의 스웜 모드 해제
 $ docker swarm leave&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;특정 워커 노드가 leave 명령어로 스웜 모드를 해제하면 매니저 노드는 해당 워커 노드의 상태를 Down으로 인지할 뿐 자동으로 워커 노드를 삭제하지 않는다. 따라서 매니저 노드에서 docker node rm 명령어를 사용해서 해당 워커 노드를 삭제해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661759045209&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// (매니저 노드에서) 워커 노드 삭제
$ docker node rm [HOSTNAME]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;매니저 노드가 스웜 모드를 해제하려면, docker swarm leave 명령어에 --force 옵션을 추가해줘야한다. 단, 매니저 노드가 단 한 개 존재할 때 매니저 노드를 삭제하면 해당 스웜 클러스터는 더 이상 사용하지 못하는 상태가 된다. 따라서 매니저 노드를 삭제할 때는 신중을 기해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661759122459&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// (매니저 노드 상에서 삭제)
$ docker swarm leave --force&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;워커 노드를 매니저 노드로 변경하려면, docker node promote 명령어를 사용한다. 이와 반대로 매니저 노드에서 워커 노드로 변경하려면 docker node demote 명령어를 사용하면 된다. 단, 매니저 노드가 1개 일 때엔 demote 명령어를 사용할 수 없다. 매니저 리더 노드에 demote 명령어를 사용하면 다른 매니저 노드 중 새로운 리더를 산출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661759375666&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// (매니저 노드 상에서 입력되는 명령어들)

// 워커 -&amp;gt; 매니저
# docker node promote [HOSTNAME]

// 매니저 -&amp;gt; 워커
# docker node demote [HOSTNAME]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;References&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392291&quot;&gt;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392291&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1661749962760&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;시작하세요! 도커/쿠버네티스 - 교보문고&quot; data-og-description=&quot;친절한 설명으로 쉽게 이해하는 컨테이너 관리 | ★ 이 책의 구성 ★◎ 도커의 기본 사용 방법을 익힘으로써 컨테이너의 기본 개념을 학습합니다. (1부 1장, 1부 2장)◎ 도커 스웜 모드를 통해 서&quot; data-og-host=&quot;www.kyobobook.co.kr&quot; data-og-source-url=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392291&quot; data-og-url=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392291&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/v2l2F/hyPBJAL8GZ/gYLBxr9ff3w7isyMPYwwyK/img.jpg?width=800&amp;amp;height=1010&amp;amp;face=0_0_800_1010&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392291&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392291&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/v2l2F/hyPBJAL8GZ/gYLBxr9ff3w7isyMPYwwyK/img.jpg?width=800&amp;amp;height=1010&amp;amp;face=0_0_800_1010');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;시작하세요! 도커/쿠버네티스 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;친절한 설명으로 쉽게 이해하는 컨테이너 관리 | ★ 이 책의 구성 ★◎ 도커의 기본 사용 방법을 익힘으로써 컨테이너의 기본 개념을 학습합니다. (1부 1장, 1부 2장)◎ 도커 스웜 모드를 통해 서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Devops/도커, 쿠버네티스</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/38</guid>
      <comments>https://circle-lab.tistory.com/38#entry38comment</comments>
      <pubDate>Mon, 29 Aug 2022 16:53:47 +0900</pubDate>
    </item>
    <item>
      <title>[Lombok] @NoArgsConstructor / @RequiredArgsConstructor /  @AllArgsConstructor 의 차이</title>
      <link>https://circle-lab.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 &amp;amp; 스프링부트는 많은 개발자들이 오래전부터 이용한 프레임워크이고, 대부분의 프로젝트에서 롬복 라이브러리를 사용해서 많은 개발자들이 단순 노동을 대신해서 효율적으로 코드를 짜고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 스프링 &amp;amp; 스프링부트로 개발한다면, 자주 사용되는 롬복 라이브러리에 대해서도 잘 이해해둘 필요가 있다. 최근 개발자들은 매번 스프링 빈들간에 의존성 주입을 해주거나 특정 Class의 생성자를 만드는 것을 단순 노동이라 여겨 위의 3가지 어노테이션을 잘 활용해서 중복되는 코드를 작성하는 것을 지양하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이제 본론으로 들어가서, 각 어노테이션이 의미하는 바를 알아보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@NoArgsConstructor 의미&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스프링에서 사용되는 대부분의 어노테이션은 해당 어노테이션이 동작하는 것을 잘 표현하는 어노테이션 이름을 가진다. 이름에서 알 수 있듯이, 이 어노테이션은 파라미터가 없는 생성자를 만들어준다. 코드로 설명해보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1661255718527&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor
public class TestData {
    private String data;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661255732194&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootTest
public class TestDataTest {
    @Test
    public void NoArgsConstructorTest() {
        TestData testData = new TestData();
        System.out.println(testData.getData());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AAXeR/btrKoS49aGX/SQOX6NbTcjEV2CBTGP9xKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AAXeR/btrKoS49aGX/SQOX6NbTcjEV2CBTGP9xKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AAXeR/btrKoS49aGX/SQOX6NbTcjEV2CBTGP9xKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAAXeR%2FbtrKoS49aGX%2FSQOX6NbTcjEV2CBTGP9xKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;130&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;결과는 다음과 같다. 테스트 코드에서 알 수 있듯이 NoArgsConstructor 어노테이션이 있다면, 파라미터가 없어도 생성자가 알아서 생기는 것이다. 즉, 위에서 &lt;b&gt;@NoArgsConstructor&lt;/b&gt;가 있는 것은 아래와 같은 파라미터 없는 생성자가 생기는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661255837112&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
public class TestData {
    private String data;

    public TestData() {}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;&lt;u&gt;&lt;b&gt;@NoArgsConstructor를 사용할 때 주의해야할 점&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 내 필드 중 final로&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;생성되어 있는 경우에는 필드를 초기화 할 수 없기 때문에, 생성자를 만들 수 없고&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에러가 발생한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이 때는 @NoArgsConstructor(force = true) 옵션을 이용해서 final 필드를 0, false, null 등으로 초기화를 강제로 시켜서 생성자를 만들 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;@NonNull 같이 필드에 제약조건이 설정되어 있는 경우, 생성자 내 null-check 로직이 생성되지 않습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;후에 초기화를 진행하기 전까지 null-check 로직이 발생하지 않는 점을 염두하고 코드를 개발해야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@AllArgsConstructor 의미&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 어노테이션이 있으면, 클래스 내 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;존재하는 모든 필드에 대한 생성자를 자동으로 생성해준다.&lt;/span&gt; 변수를 파라미터로 가지는 생성자를 생성하는 것이다. 또한,&lt;span style=&quot;color: #000000;&quot;&gt; 필드 중에서 @NonNull 어노테이션이 적혀있다면 생성자&lt;span&gt;&amp;nbsp;&lt;/span&gt;내에서 null-check 로직을 자동적으로 생성해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661256645869&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor
@AllArgsConstructor
public class TestData {
    @NonNull
    private String name;
    private String email;
    private String phoneNumber;
    private String description;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;i&gt;정상적으로 동작하는 코드&lt;/i&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661256698828&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test
    public void AllArgsConstructorTest() {
        TestData testData = new TestData(&quot;손흥민&quot;, &quot;aaa@naver.com&quot;, &quot;010-1111-1111&quot;, &quot;축구 잘해요!&quot;);
        System.out.println(testData.getName());
        System.out.println(testData.getEmail());
        System.out.println(testData.getPhoneNumber());
        System.out.println(testData.getDescription());
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;i&gt;비정상적으로 동작하는 코드&amp;nbsp;&lt;/i&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661256660579&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test
    public void AllArgsConstructorTestNullCheck() {
        TestData testData = new TestData(null, &quot;aaa@naver.com&quot;, &quot;010-1111-1111&quot;, &quot;축구 잘해요!&quot;);
        System.out.println(testData.getName());
        System.out.println(testData.getEmail());
        System.out.println(testData.getPhoneNumber());
        System.out.println(testData.getDescription());
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HkCXZ/btrKp5vVa1Z/8IiIWwA35hjSwUak1kRVB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HkCXZ/btrKp5vVa1Z/8IiIWwA35hjSwUak1kRVB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HkCXZ/btrKp5vVa1Z/8IiIWwA35hjSwUak1kRVB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHkCXZ%2FbtrKp5vVa1Z%2F8IiIWwA35hjSwUak1kRVB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;898&quot; height=&quot;153&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@RequiredArgsConstructor 의미&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;이 어노테이션은 아직 초기화되지 않은 모든 final 필드, @NonNull이 마크돼있는 모든 필드들에 대한 생성자를 자동으로 생성해준다. 즉 이름에서 알 수 있듯이, 필요한 생성자를 자동으로 생성해주는 어노테이션이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;@NonNull로 마크 돼있는 필드들은 null-check가 추가적으로 생성되며, @NonNull이 마크되어 있지만, 파라미터에서 null값이 들어온다면 생성자에서 NullPointerException이 발생한다. &lt;/span&gt;&lt;u&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파라미터의 순서는 클래스에 있는 필드 순서에 맞춰서 생성된다.&lt;/span&gt;&lt;/u&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 코드로 예시를 들어보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661257332305&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@RequiredArgsConstructor
@AllArgsConstructor
public class TestData {
    @NonNull
    private String name;

    private String email;
    private String phoneNumber;
    private String description;

    private final String rData1;
    private final String rData2;
    private final String rData3 = &quot;pre-initialized&quot;;

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661257350857&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test
    public void RequiredArgsConstructorTest() {
        /**
         * 초기화 되지 않은 final 필드와 NonNull 어노테이션이 붙어 있는 필드에 대한 생성자를 만들어준다.
         */
        TestData testData = new TestData(&quot;name&quot;, &quot;rd1&quot;, &quot;rd2&quot;);

        System.out.println(testData.getName());
        System.out.println(testData.getRData1());
        System.out.println(testData.getRData2());

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RweUJ/btrKp9LDvno/zJsVdz7QqgQkjiCpQWIhd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RweUJ/btrKp9LDvno/zJsVdz7QqgQkjiCpQWIhd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RweUJ/btrKp9LDvno/zJsVdz7QqgQkjiCpQWIhd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRweUJ%2FbtrKp9LDvno%2FzJsVdz7QqgQkjiCpQWIhd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;224&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위의 결과를 보면 알 수 있듯이, 이미 초기화돼있는 필드에 대한 생성자가 생기는 것이 아니라, 초기화되지 않은 final 필드와 @NonNull 어노테이션이 붙어있는 필드에 대한 생성자가 생기게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스프링 &amp;amp; 스프링부트를 사용해서 개발할 때, 해당 어노테이션에 대해서 자세히 알아야 왜 @RequiredArgsConstructor 어노테이션을 붙였을 때 의존성 주입이 되는지 혹은 @NoArgsConstructor 와 @AllArgsConstructor를 도메인 클래스나 DTO 클래스에서 어떻게 사용할지에 대한 고민을 해결해줄 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;References&lt;/i&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://projectlombok.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://projectlombok.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dingue.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dingue.tistory.com/14&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Framework &amp;amp; Library/Spring &amp;amp; SpringBoot</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/37</guid>
      <comments>https://circle-lab.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 23 Aug 2022 21:27:06 +0900</pubDate>
    </item>
    <item>
      <title>[도커/쿠버네티스] 도커 데몬</title>
      <link>https://circle-lab.tistory.com/36</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;도커의 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 명령어는 /usr/bin/docker 에 위치한 파일을 통해 사용되고 있다. 하지만, 도커 엔진의 프로세스는 /usr/bin/dockerd 파일로 실행되고 있다. 이렇게 되는 이유는 docker 명령어가 실제 도커 엔진이 아닌 클라이언트로서의 도커이기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;도커의 구조&lt;/b&gt;는 크게 두 가지로 나뉜다. 클라이언트로서의 도커, 서버로서의 도커로 나뉜다. 실제로 컨테이너를 생성하고 실행하며 이미지를 관리하는 주체는 &lt;b&gt;도커 서버&lt;/b&gt;이고, 이는 dockerd 프로세스로서 동작한다. 도커 엔진은 외부에서 API 입력을 받아 도커 엔진의 기능을 수행하는데, &lt;b&gt;도커 프로세스가 실행되어 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;서버&lt;/span&gt;로서 입력을 받을 준비가 된 상태&lt;/b&gt;를 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;도커 데몬&lt;/b&gt;&lt;/span&gt;이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다른 하나는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;도커 클라이언트&lt;/b&gt;&lt;/span&gt;이다. 도커 데몬은 API 입력을 받아 도커 엔진의 기능을 수행하는데, 이 API를 사용할 수 있도록 CLI를 제공하는 것이 도커 클라이언트이다. 사용자가 docker로 시작하는 명령어를 입력하면 도커 클라이언트를 사용하는 것이며, 도커 클라이언트는 입력된 명령어를 로컬에 존재하는 도커 데몬에게 API로서 전달한다. 이 때 도커 클라이언트는 /var/run/docker.sock 에 위치한 유닉스 소켓을 통해 도커 데몬의 API을 호출한다. 도커 클라이언트가 사용하는 유닉스 소켓은 같은 호스트 내에 있는 도커 데몬에게 명령을 전달할 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;도커 데몬 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 데몬은 일반적으로 아래와 같은 명령어로 시작, 정지할 수 있다. &lt;b&gt;우분투&lt;/b&gt;에서는 도커가 설치되면 자동으로 서비스로 등록되므로 호스트가 재시작되더라도 자동으로 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1658218969215&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ service docker start

$ service docker stop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;레드햇 계열의 운영체제&lt;/b&gt;는 도커를 설치해도 자동으로 실행되도록 설정되지는 않습니다. 도커를 자동으로 실행하도록 설정하려면 아래의 명령어로 docker 서비스를 활성화합니다. 자동으로 실행하도록 설정하려면 docker 서비스를 활성화한다.&lt;/p&gt;
&lt;pre id=&quot;code_1658219025711&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ systemctl enable docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 서비스는 dockerd 로 도커 데몬을 실행한다. 그러나 서비스를 사용하지 않고 직접 도커 데몬을 실행할 수 있다. 도커 서비스를 정지한 뒤 명령어로 도커를 직접 실행해본다. (아래 명령어)&lt;/p&gt;
&lt;pre id=&quot;code_1658219128344&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ service docker stop
$ dockerd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dockerd를 입력하면 도커 데몬이 실행된다. Ctrl+C입력하면 종료 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커는 특정 스토리지 벡엔드 기술을&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;도커&lt;span&gt;&amp;nbsp;&lt;/span&gt;컨테이너와&lt;span&gt;&amp;nbsp;&lt;/span&gt;이미지를 저장하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일부 OS는 도커를 설치할 때 기본적으로 사용되도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정된 스토리지 드라이버가 있는데, 우분투와 같은 데비안 계열의 OS는 overlay2를, 구버전의 CentOS와 같은 OS는 devicemapper를 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 docker info 명령어로 확인할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;도커 모니터링&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 데몬을 모니터링하는 데에는 여러 가지 이유가 있다. 많은 수의 도커 서버를 효율적으로 관리하기 위해서일 수도 있고, 도커로 컨테이너 애플리케이션을 개발하다가 문제가 생겼을 때 그 원인을 찾아내기 위해서일 수도 있으며 도커를 PaaS로써 제공하기 위해 실시간으로 도커 데몬의 상태를 체크해야할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다양한 목적에 부합하는 모니터링 방법은 매우 많다. 그 중에는 도커 엔진 자체가 지원하는 모니터링 기능도 있고, 도커 프로젝트에서 지원하는 상용솔루션 및 각종 오픈소스 대시보드도 있다. 아래에서는 하나의 컨테이너뿐만 아니라 도커 데몬 자체를 모니터링하는 방법을 알아보겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;도커 데몬 디버그 모드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 데몬에서 어떤 일이 일어나고 있는지 가장 확실하고 정확하게, 그리고 자세히 알아내는 방법은 도커 데몬을 디버그 옵션으로 실행하는 것이다. 이렇게 하면 Remote API의 입출력뿐만 아니라 로컬 도커 클라이언트에서 오가는 모든 명령어를 로그로 출력한다. 디버그 모드는 도커 데몬을 실행할 때 -D 옵션을 추가해서 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eWwr0N/btrIpHbTQw1/4SrG33k6ESN2Km0KPgpU80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eWwr0N/btrIpHbTQw1/4SrG33k6ESN2Km0KPgpU80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eWwr0N/btrIpHbTQw1/4SrG33k6ESN2Km0KPgpU80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeWwr0N%2FbtrIpHbTQw1%2F4SrG33k6ESN2Km0KPgpU80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1417&quot; height=&quot;102&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디버그 모드는 도커 데몬에 문제가 생겼을 때 무엇이 잘못됐는지 확인하는 가장 좋은 수단이다. 그러나 로그에는 원하지 않는 정보까지 너무 많이 출력되며, 호스트에 있는 파일을 읽거나 도커 데몬을 포그라운드 상태로 실행해야 한다는 단점이 있으므로 이 방법만으로는 뭔가 조금 부족해보인다. &lt;u&gt;&lt;b&gt;도커 명령어로 도커 데몬을 모니터링하는 방법에 대해 알아본다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;events, stats, system df 명령어&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;events 명령어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 사용하는 가장 쉬운 방법은 도커 자체가 제공하는 기능을 사용하는 것이며, &lt;b&gt;events 명령어&lt;/b&gt;도 도커가 기본으로 제공하는 명령어이다. events 명령어는 도커 데몬에 어떤 일이 일어나고 있는지를 실시간 스트림 로그로 보여준다. 다음 명령어 중 하나를 입력하면 &lt;b&gt;도커 데몬이 수행한 명령어의 결과를 실시간으로 볼 수 있다&lt;/b&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1658924079466&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker events
$ docker system events&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 명령어를 입력한 직후에는 어떠한 이벤트도 도커 데몬에 발생하지 않았으므로 아무것도 출력되지 않는다. 새로운 터미널을 연 뒤에 ubuntu:14.04 이미지를 pull 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지의 pull이 완료되면 docker events 명령어를 실행했던 터미널에서 다음과 같은 명령어가 출력되는 것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;37&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ua4SS/btrIjLUOm2N/RbfhyQ9j78tr72zKwCYYk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ua4SS/btrIjLUOm2N/RbfhyQ9j78tr72zKwCYYk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ua4SS/btrIjLUOm2N/RbfhyQ9j78tr72zKwCYYk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUa4SS%2FbtrIjLUOm2N%2FRbfhyQ9j78tr72zKwCYYk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;37&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;37&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;stats 명령어&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;docker stats 명령어는 실행 중인 모든 컨테이너의 자원 사용량을 스트림으로 출력한다. stats 명령어도 다음과 같이 간단히 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stats 명령어는 실행 중인 모든 컨테이너의 CPU, 메모리 제한 및 사용량, 네트워크 입출력(I/O), 블록 입출력(하드웨어 입출력) 정보를 출력한다. 기본적으로 스트림 형태로 출력되며, 스트림이 아닌 한 번만 출력하는 방식으로 사용하고 싶다면 --no-stream 옵션을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1658924615070&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker stats --no-stream&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;system df 명령어&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;system df 명령어는 도커에서 사용하는 이미지, 컨테이너, 로컬 볼륨의 총 개수 및 사용 중인 개수, 크기, 삭제함으로써 확보 가능한 공간을 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cz2VZ1/btrIkpjyfJf/78yyDH0Fivn681LVF1wXJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cz2VZ1/btrIkpjyfJf/78yyDH0Fivn681LVF1wXJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cz2VZ1/btrIkpjyfJf/78yyDH0Fivn681LVF1wXJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcz2VZ1%2FbtrIkpjyfJf%2F78yyDH0Fivn681LVF1wXJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;543&quot; height=&quot;103&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;지금 현재 내 컴퓨터에서의 총 이미지의 개수는 10개이고, 사용 중인 이미지는 5개이고 이미지가 차지하는 공간은 2.49GB인 것을 알 수 있다. &lt;/span&gt;RECLAIMABLE 항목은 사용 중이지 않은 이미지를 삭제함으로써 확보할 수 있는 공간을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용 중이지 않은 컨테이너와 볼륨은 각각 아래의 명령어로 한꺼번에 삭제할 수 있다. docker image prune 를 사용하면 사용 중이지 않은 댕글링(dangling) 이미지를 삭제한다.&lt;/p&gt;
&lt;pre id=&quot;code_1658924953559&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 사용 중이지 않는 컨테이너 삭제
$ docker container prune

// 사용 중이지 않는 볼륨 삭제
$ docker volume prune

// 사용 중이지 않는 이미지 삭제
$ docker image prune&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CAdvisor&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CAdvisor는 구글이 만든 컨테이너 모니터링 도구로, 컨테이너로서 간단히 설치할 수 있는 컨테이너별 실시간 자원 사용량 및 도커 모니터링 정보 등을 시각화해서 보여준다. CAdvisor는 오픈소스로서 깃허브에서 소스코드로 사용할 수 있으며, 도커 허브에서 도커 이미지로도 배포되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CAdvisor를 사용하기 위해 다음 명령어를 입력한다. CAdvisor는 컨테이너 에이전트의 형태로 도커 모니터링에 필요한 자료를 수집한다.&lt;/p&gt;
&lt;pre id=&quot;code_1658925227460&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:ro \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8080:8080 \
--detach=true \
--name=cadvisor \
google/cadvisor:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실행한 Public IPv4에 8080번 포트로 접속하면, 아래와 같은 화면이 나온다. 이렇게 화면이 정상적으로 나오면 CAdvisor 컨테이너가 정상적으로 생성된 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1001&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KvnzE/btrIkorpzy4/0LUlMkrDcMIDq5onPBKLt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KvnzE/btrIkorpzy4/0LUlMkrDcMIDq5onPBKLt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KvnzE/btrIkorpzy4/0LUlMkrDcMIDq5onPBKLt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKvnzE%2FbtrIkorpzy4%2F0LUlMkrDcMIDq5onPBKLt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1918&quot; height=&quot;1001&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1001&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CAdvisor 에서는 생성된 모든 컨테이너의 자원 사용량을 확인할 수 있을 뿐만 아니라 도커 데몬의 정보, 상태, 호스트의 자원 사용량까지 한 번에 확인할 수 있다. IP 주소와 8080번 포트로 접속했을 때 확인할 수 있는 페이지는 호스트의 프로세스, 자원 사용량 등을 보여준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[/SubContainers]의 [/docker]를 클릭하면 도커 데몬의 정보, 컨테이너의 목록을 보여주는 페이지로 이동한다. [SubContainers] 항목의 컨테이너 이름을 컨테이너의 자원 사용률도 실시간으로 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CAdvisor의 대시보드는 60초간의 모니터링 정보만 보여주지만 InfluxDB나 Prometheus 등과 같이 사용하면 장기간의 모니터링 정보를 수집하고 분석할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 짚고 넘어가야할 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; 어떻게 CAdvisor는 이렇게 많은 정보를 사용자에게 보여줄 수 있는 것인가??&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;답은 도커 데몬의 정보를 가져올 수 있는 호스트의 모든 디렉토리를 CAdvisor 컨테이너에 볼륨으로서 마운트했기 때문&lt;/b&gt;&lt;/span&gt;이다. /var/run 에는 도커를 로컬에서 제어하기 위한 유닉스 소켓이 있고, /sys에는 도커 컨테이너를 위한 cgroup 정보가 저장돼 있으며 /var/lib/docker에는 도커의 컨테이너, 이미지 등이 파일로 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 CAdvisor는 단일 도커 호스트만을 모니터링할 수 있다는 한계가 있다. 여러 개의 호스트로 도커를 사용할 수 있으며, 이를 기반으로 PaaS(Platform as a Service) 같은 도커 클러스터를 구축했다면 단일 CAdvisor 컨테이너는 용도에 맞지 않을 수 있다. 이를 위해서 보통은 &lt;b&gt;Kubernetes나 Docker Swarm 모드&lt;/b&gt; 등과 같은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;오케스트레이션 툴(Orchestration)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;을 설치한 뒤에 &lt;b&gt;프로메테우스(Prometheus), InfluxDB&lt;/b&gt; 등을 이용해 &lt;b&gt;여러 호스트의 데이터를 수집하는 것이 일반적&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;컨테이너 배포 관리는 흔히 컨테이너 오케스트레이션 (Container Orchestration) 이라고 불립니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;컨테이너 오케스트레이션의 목적은 여러 컨테이너의 배포 프로세스를 최적화 하는데 있으며, 이것은 컨테이너와 호스트의 수가 증가함에 따라 점점 더 가치가 있게 됩니다. 이러한 유형의 자동화를 오케스트레이션이라고 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Devops/도커, 쿠버네티스</category>
      <author>땡글이B</author>
      <guid isPermaLink="true">https://circle-lab.tistory.com/36</guid>
      <comments>https://circle-lab.tistory.com/36#entry36comment</comments>
      <pubDate>Tue, 19 Jul 2022 17:46:42 +0900</pubDate>
    </item>
  </channel>
</rss>