<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Standing on the shoulders of giants</title>
    <link>https://rura6502.tistory.com/</link>
    <description>개발자 블로그</description>
    <language>ko</language>
    <pubDate>Thu, 9 Apr 2026 09:03:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>rura6502</managingEditor>
    <image>
      <title>Standing on the shoulders of giants</title>
      <url>https://tistory1.daumcdn.net/tistory/2533192/attach/7e0fd73bdfc14109a71914a70679b45d</url>
      <link>https://rura6502.tistory.com</link>
    </image>
    <item>
      <title>asdf</title>
      <link>https://rura6502.tistory.com/entry/asdf</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://asdf-vm.com/guide/introduction.html&quot;&gt;https://asdf-vm.com/guide/introduction.html&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Guide&lt;/h1&gt;
&lt;h2&gt;What is asdf?&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;asdf는 툴 버전 매니저이다. 모든 툴 버전 정의는 하나의 파일(.tool-versions) 파일에 포함된다. 사용자는 깃 레포에서 이 것을 확인하여 팀에게 공유하고, 모든 팀원들이 정확하게 같은 버전의 툴을 사용할 수 있도록 보증할 수 있다.&lt;/p&gt;
&lt;p&gt;오래된 작업 방식은 여러 CLI 버전 매니저, 각각의 구분된 API, 설정파일, 구현(ex. @PATH 조작, shims, 환경변수 등등)을 요구했다. asdf는 개발 워크플로우를 단순화하기위해 싱글 인터페이스, 환경설정을 제공하고 간단한 플러그인 인터페이스를 통해 모든 도구와 런타임을 확장할 수 있도록 제공한다.&lt;/p&gt;
&lt;h3&gt;How It Works&lt;/h3&gt;
&lt;p&gt;asdf는 사용자의 쉘 설정에 셋업되면 특정 툴들을 관리하기위해 플러그인이 설치된다. 플러그인을 통해 툴이 설치되면 그 각각의 툴을 위한 shims를 가지는 실행가능한 파일이 생성된다. 사용자는 실행파일을 실행하면 asdf가 .tool-versions에 설정된 툴들의 버전을 구분해주는 shim이 실제로 실행된다.&lt;/p&gt;
&lt;h3&gt;Related Projects&lt;/h3&gt;
&lt;h4&gt;nvm / n / rbenv etc&lt;/h4&gt;
&lt;p&gt;nvm, n, rbenv와 같은 툴들은 이들 툴들에 의해 설치된 실행파일들을 위한 shim을 생성하는 쉘 스크립트이다.&lt;br&gt;asdf는 매우 유사하고 이러한 툴/런타임 버전 관리의 영역들과 경쟁하도록 만들어졌다. asdf의 다른점은 툴/런타임 별 매니저를 필요에따라 삭제할 수 있고, 레포의 *-version 파일 별로, 매니저별 다른 명령어들도 필요에 따라 삭제할 수 있는 플러그인 시스템이다.&lt;/p&gt;
&lt;h4&gt;direvn&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;현재 디렉토리에 따라 환경변수를 로드하고 언로드할 수 잇는 새로운 기능으로 기존 쉘을 보강한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;asdf는 환경변수를 관리하지 않는다. 그러나 asdf-direnv 플러그인을 사용해서 direvn의 행위를 asdf와 통합할 수 있다.&lt;/p&gt;
&lt;h4&gt;Homebrew&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;The Missing Package Manager for macOS (or Linux)&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Homebrew는 사용자의 패키지와 업스트림 의존성(upstream dependencies)을 관리한다. asdf는 업스트림 의존성을 관리하지 않는다. 패지키관리자가 아니고 의존성 리스트를 작게 유지하려고 노력하기 때문에 그것은 사용자가 해야한다.&lt;/p&gt;
&lt;h4&gt;NixOS&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Nix는 패키지 관리와 시스템 설정에 unique approach를 가지고 있는 도구&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;NixOS는 각각의 툴에 대한 의존성 트리 전체 패키지의 정확한 버전을 관리함으로써 환경을 truly하게 재연하는 것을 목표로 하는 툴이다. 몇몇 기능들은 asdf가 하지 못한다. NixOS는 자체 프로그래밍 언어, 많은 CLI tools, 6만개 이상의 패키지 컬렉션으로 동작한다.&lt;br&gt;하지만 asdf는 upstream dependency들을 관리하지않으며 패키지 매니저가 아니다.&lt;/p&gt;
&lt;h2&gt;Why use asdf?&lt;/h2&gt;
&lt;p&gt;asdf는 플러그인 시스템을 통해 많은 도구들로 팀들이 정확하게 같은 버전의 툴을 사용하는 것을 보증하고 사용자의 쉘 설정에포함되는 단일 쉘 스크립트의 단순함과 친숙함을 제공한다.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;asdf 설치본은 아래 항목을 포함하고 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;디펜던시 설치&lt;/li&gt;
&lt;li&gt;asdf core 다운로드&lt;/li&gt;
&lt;li&gt;asdf 설치&lt;/li&gt;
&lt;li&gt;사용자가 관리 하고자하는 각각의 툴, 런타임을 위한 플러그인 설치본&lt;/li&gt;
&lt;li&gt;툴, 런타임 버전 설치본&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.tool-versions&lt;/code&gt; 설정 파일들을 통한 글로벌/프로젝트 버전 관리 설정&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1. Install Dependencies&lt;/h3&gt;
&lt;p&gt;asdf 는 주로 git과 curl 을 요구한다. 사용자의 패키지 매니저를 구동하기 위해 아래 전체 커맨트 리스트가 있다.(몇몇은 이후 단계에서 자동으로 설치될 수 있다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ph1D5/btsCc2TImcN/mhqB2d5aLFiLYfyCOylEa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ph1D5/btsCc2TImcN/mhqB2d5aLFiLYfyCOylEa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ph1D5/btsCc2TImcN/mhqB2d5aLFiLYfyCOylEa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fph1D5%2FbtsCc2TImcN%2FmhqB2d5aLFiLYfyCOylEa0%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;632&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;사용자의 시스템 설정에 따라 &lt;code&gt;sudo&lt;/code&gt; 가 필요할 수 있다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;2. Download asdf&lt;/h3&gt;
&lt;h4&gt;Official Donwload&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.13.1&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;Community Supported Download Methods&lt;/h4&gt;
&lt;p&gt;공식 git을 이용하는 것을 강력 추천한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Howbrew : &lt;code&gt;brew install asdf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pacman : &lt;code&gt;git clone https://aur.archlinux.org/asdf-vm.git &amp;amp;&amp;amp; cd asdf-vm &amp;amp;&amp;amp; makepkg -si&lt;/code&gt; 또는 선호에 따라 AUR helper&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Install asdf&lt;/h3&gt;
&lt;p&gt;여기에 구성에 영향을 미치는 쉘, OS, 설치 방법에 대한 다른 조합들이 있다. 너의 시스템과 가장 매칭되는 selection을 expand해라.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공식문서 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;asdf는 사용자의 프레임워크(oh-my-zsh 등)을 source하고, &lt;code&gt;$PATH&lt;/code&gt;를 설정한 후 source 해야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Warninig&lt;br&gt;macOS에서 Bash, Zsh 쉘은 시작되면서 자동으로 &lt;code&gt;path_helper&lt;/code&gt;라는 유틸리티를 호출한다. &lt;code&gt;path_helper&lt;/code&gt;는 &lt;code&gt;PATH(and MANPATH)&lt;/code&gt;를 rearrange item 해서 특정 순서가 요구되는 툴들의 동작에 inconsistent를 일으킬 수 있다. 이 문제를 해결하기 위해서 맥상의 &lt;code&gt;asdf&lt;/code&gt;는 기본적으로 &lt;code&gt;PATH&lt;/code&gt; 엔트리들의 가장 앞쪽에(가장 높은 우선위를 가지기 위해) 강제로 추가된다. 이것은 &lt;code&gt;ASDF_FORCE_PREPEND&lt;/code&gt; 변수를 통해 설정 가능하다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;PATH의 변경을 적용하기 위해 쉘을 재시작해라. 일반적으로 새로운 탭을 열면 된다.&lt;/p&gt;
&lt;h3&gt;Core Installation Complete!&lt;/h3&gt;
&lt;p&gt;asdf 코어 설치가 완료되었다.&lt;/p&gt;
&lt;p&gt;asdf는 플러그인을 설치하고 툴을 설치하고 그 툴의 버전을 관리해야 유용하게 사용할 수 있다. 계속되는 가이드는 이것을 어떻게 하는지 알려준다.&lt;/p&gt;
&lt;h3&gt;4. Install a Plugin&lt;/h3&gt;
&lt;p&gt;시연을 위해 asdf-nodejs를 통해 nodejs를 설치&amp;amp;설정할 것이다.&lt;/p&gt;
&lt;h4&gt;Plugin Dependencies&lt;/h4&gt;
&lt;p&gt;각각의 플러그인들은 종속성이 있기 때문에 우리는 그 종속성이 리스팅되어있어야 하는 플러그인 레포를 체크해야 한다. asdf-nodesjs는 여기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debian apt-get install dirmngr gpg curl gawk&lt;/li&gt;
&lt;li&gt;CentOS/ Rocky Linux/ AlmaLinux yum install gnupg2 curl gawk&lt;/li&gt;
&lt;li&gt;macOS brew install gpg gawk&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;몇몇 플러그인들읜 ponst-install hooks를 가지고 있기 때문에 디펜던시를 먼저 설치해야 한다.&lt;/p&gt;
&lt;h4&gt;Install the Plugin&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;5. Install a Version&lt;/h3&gt;
&lt;p&gt;이제 우리는 nodejs 플러그인을 가지고 있고 툴의 버전을 설치할 수 있다.&lt;br&gt;&lt;code&gt;asdf list all nodejs&lt;/code&gt; 명령어로 사용가능한 버전을 볼 수 있고 &lt;code&gt;asdf list all nodejs 14&lt;/code&gt;로 버전의 서브셋 목록을 볼 수 있다.&lt;br&gt;&lt;code&gt;latest&lt;/code&gt;로 가능한 버전을 볼수도 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;asdf install nodejs latest&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Note&lt;br&gt;asdf는 정확한 버전을 강제한다ㅣ &lt;code&gt;latest&lt;/code&gt;는 실행 시 가장 최신의 버전을 resolve하는 helper 이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;6. Set a Version&lt;/h3&gt;
&lt;p&gt;asdf는 &lt;code&gt;$HOME&lt;/code&gt; 디렉토리부터 현재 워킹 디렉토리까지의 모든 &lt;code&gt;.tool-versions&lt;/code&gt; 파일에 정의된 툴의 버전을 lookup 한다. lookup은 asdf 관리 도구가 를 실행될 때 just-in-time으로 실행한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;WARNING&lt;br&gt;툴의 실행을 위한 version listed 행위가 없으면 에러가 난다. &lt;code&gt;asdf current&lt;/code&gt; 는 현재 사용자의 디렉터리로부터 툴의 버전이 resolution인지, absent인지 보여주어 사용자는 어떤 툴이 실행에 실패할 것인지 확인할 수 있다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;Global&lt;/h3&gt;
&lt;p&gt;기본 전역 설정은 &lt;code&gt;$HOME/.tool-versions&lt;/code&gt;에 관리된다. 아래와 같이 전역 버전을 설정할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;asdf global nodejs latest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;$HOME/.tool-versiosn&lt;/code&gt;는 아래와 같이 볼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nodejs 16.5.0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;몇몇 OS는 파이썬같이 asdf가 아니라 시스템에 의해 관리되는 툴이 이미 설치되어 있을 수 있다. 사용자는 asdf에게 다시 시스템에 의해 관리되도록 설정해야한다. &lt;a href=&quot;https://asdf-vm.com/manage/versions.html&quot;&gt;Versions reference section&lt;/a&gt;이 도움이 가이드해줄 것이다.&lt;/p&gt;
&lt;h3&gt;Local&lt;/h3&gt;
&lt;p&gt;로컬 버전은 &lt;code&gt;$PWD/.tool-versions&lt;/code&gt; 파일(사용자의 현재 working directory)에 정의된다. 대부분 프로젝트의 깃 레포가 될 수 있다. 사용자가 원하는 디렉토리에서 실행한다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;asdf local nodejs latest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;$PWD/.tool-versions&lt;/code&gt; 는 아래와 같이 설정된다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nodejs 16.5.0&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Using Existing Tool Version Files&lt;/h3&gt;
&lt;p&gt;asdf는 다른 오래된 버전 매니저의 버전 파일을 마이그레이션하는 것을 지원한다. &lt;code&gt;rbenv&lt;/code&gt;의 &lt;code&gt;.ruby-version&lt;/code&gt;와 같은 경우이다. 이러한 지원은 플러그인 베이스로 지원된다.&lt;br&gt;&lt;code&gt;asdf-nodejs&lt;/code&gt;는 &lt;code&gt;.nvmrc&lt;/code&gt;, &lt;code&gt;.node-version&lt;/code&gt;을 통해 지원된다. 이 기능을 활성화하기 위해 사용자의 asdf configuration file인 &lt;code&gt;$HOME/.asdfrc&lt;/code&gt; 파일을 아래와 같이 수정하여야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;legacy_version_file = yes&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://asdf-vm.com/guide/getting-started.html#:~:text=See%20the-,configuration,-reference%20page%20for&quot;&gt;configuration&lt;/a&gt; 레퍼런스 페이지를 통해 더 많은 설정을 확인하여라&lt;/p&gt;
&lt;h2&gt;Guide Complete!&lt;/h2&gt;
&lt;p&gt;asdf의 Getting Started 가이드가 끝났다. 너는 이제 프로젝트의 nodejs의 버전을 관리할 수 있다. 너의 프로젝트의 각각의 툴 타입을 위해 다음 유사한 단계를 따라해라.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;asdf&lt;/code&gt;, &lt;code&gt;asdf --help&lt;/code&gt;를 실행해서 명령어를 확인하고 더 많은 명령어와 친숙해져라.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://asdf-vm.com/manage/core.html&quot;&gt;core asdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://asdf-vm.com/guide/getting-started.html#:~:text=core%20asdf-,plugins,-versions%20(of%20tools&quot;&gt;plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://asdf-vm.com/guide/getting-started.html#:~:text=plugins-,versions%20(of%20tools),-Last%20updated%3A&quot;&gt;versions(of tools)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Usage&lt;/h1&gt;
&lt;h2&gt;Core&lt;/h2&gt;
&lt;p&gt;asdf 핵심 명령어 목록은 다소 작지만 많은 워크플로우를 용이하게 할 수 있다.&lt;/p&gt;
&lt;h3&gt;Installation &amp;amp; Setup&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://asdf-vm.com/manage/core.html#:~:text=Covered%20in%20the-,Getting%20Started,-guide.&quot;&gt;Getting Started&lt;/a&gt; 가이드를 참고하라&lt;/p&gt;
&lt;h3&gt;Exsec&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;asdf exec &amp;lt;command&amp;gt; [args...]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;현재 버전의 shim 명령어를 실행&lt;/p&gt;
&lt;h3&gt;Env&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;asdf env &amp;lt;command&amp;gt; [util]&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Info&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;asdf info&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OS, Shell, asdf debug information을 출력하기 위한 헬퍼 커맨드. 버그 리포트를 만들 때 이 명령어의 결과를 공유&lt;/p&gt;
&lt;h3&gt;Reshim&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;asdf reshim &amp;lt;name&amp;gt; &amp;lt;version&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 명령어는 패키지의 현재 버전의 shim들을 재생성한다. 기본적으로 shim은 툴을 설치하는 동안 플로그인에 의해 생성된다. &lt;code&gt;npm CLI&lt;/code&gt;같은 몇몇 툴들은 실행가능한 글로벌 설치본을 제공한다. 예를들어 &lt;code&gt;npm install -g yarn&lt;/code&gt;으로 &lt;code&gt;Yarn&lt;/code&gt;을 설치하는 것이다. 이 실행가능한 명령어는 플러그인 라이프사이클에 의해 설치되지 않기 떄문에 이를 위한 shim이 아직까지는 존재하지 않는다. &lt;code&gt;asdf reshum nodejs &amp;lt;version&amp;gt;&lt;/code&gt;은 &lt;code&gt;yarn&lt;/code&gt; 처럼 어떤 새로운 실행가능 명령 shim을 강제로 recalculation 한다.&lt;/p&gt;
&lt;h3&gt;Shim-versions&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;asdf shum-versions &amp;lt;command&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;명령어를 위한 shim을 제공하는 플러그인과 버전 목록을 리스팅한다.&lt;/p&gt;
&lt;p&gt;예를들어 Node.js는 &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt; 두 명령어를 설치한다. &lt;code&gt;asdf-nodejs&lt;/code&gt;로 더 많은 툴의 버전이 설치되어있다면 &lt;code&gt;shim-versions&lt;/code&gt;는 이렇게 리턴될 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ asdf shim-versions node
nodejs 14.8.0
nodejs 14.17.3
nodejs 16.5.0


$ asdf shim-versions npm
nodejs 14.8.0
nodejs 14.17.3
nodejs 16.5.0&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Update&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;asdf plugin update --all&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 사사용자가 특정 패키지의 업데이트를 원하면 아래와 같이 하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;asdf plugin update &amp;lt;name&amp;gt;
# asdf plugin update erlang &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 update 명령어는 플러그인 레포지터리의 default branch of the origin 의 가장 최근 커밋을 가져온다. 버전이 적용된 플러그인과 업데이는 현재 개발중이다.&lt;/p&gt;
&lt;h3&gt;Remove&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;asdf plugin remove &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;플러그인을 지우는 것은 플러그인에 의해 만들어진 모든 툴의 설치본을 지운다. 이것은 툴의 많은 미사용 버전을 cleaning/pruning하기 위한 간편한 방법이다.&lt;/p&gt;
&lt;h3&gt;Syncing the asdf Short-name Repository&lt;/h3&gt;
&lt;p&gt;short-name repo는 사용자의 로컬 머신과 싱크되고 주기적으로 refresh 된다. 싱크를 확인하는 방법은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;싱크 이벤트를 명렁어로 트리거한다.&lt;ul&gt;
&lt;li&gt;&lt;code&gt;asdf plugin add &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asdf plugin list all&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disable_plugin_short_name_repository&lt;/code&gt;가 &lt;code&gt;yes&lt;/code&gt;로 설정되어있다면 동기화가 중단된다. 더 많은 정보는 &lt;a href=&quot;https://asdf-vm.com/manage/configuration.html&quot;&gt;asdf config docs&lt;/a&gt;를 참고해라.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X&lt;/code&gt;분 동안 동기화가 이루어지지 않았다면, 동기화가 발생한다.&lt;ul&gt;
&lt;li&gt;&lt;code&gt;X&lt;/code&gt;는 기본 &lt;code&gt;60&lt;/code&gt; 이지만 &lt;code&gt;.asdfrc&lt;/code&gt;의 &lt;code&gt;plugin_repository_last_check_duration&lt;/code&gt; 옵션에서 설정할 수 있다. asdf config docs를 참고해라.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Versions&lt;/h2&gt;
&lt;h3&gt;Install Version&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;asdf install &amp;lt;name&amp;gt; &amp;lt;version&amp;gt;
# asdf install erlang 17.3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;플러그인이 소스로부터의 컴파일, 다운로드를 지원한다면 &lt;/p&gt;</description>
      <category>통번역</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/76</guid>
      <comments>https://rura6502.tistory.com/entry/asdf#entry76comment</comments>
      <pubDate>Sat, 9 Dec 2023 00:34:33 +0900</pubDate>
    </item>
    <item>
      <title>Google Cloud StudyJam - [Coursera]Architecting with Google Kubernetes Engine: Foundations/Worload/Productions</title>
      <link>https://rura6502.tistory.com/entry/Google-Cloud-StudyJam-CourseraArchitecting-with-Google-Kubernetes-Engine-FoundationsWorloadProductions</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본 내용은 2022년 10월 진행한 Google Cloud StudyJab에서 제공하는 Coursera의&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Architecting with Google Kubernetes Engine: Foundations&lt;/li&gt;
&lt;li&gt;Architecting with Google Kubernetes Engine: Workload&lt;/li&gt;
&lt;li&gt;Architecting with Google Kubernetes Engine: Productions&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 정리하였음&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Foundation&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cloud Computing and Google Cloud&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드의 5가지 fundamental&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;On-demand and self-service: No human intervention needed to get resources&lt;/b&gt;&lt;br /&gt;주문형, 셀프 서비스. 별도 사람의 개입 없이 자동화된 인터페이스를 사용하여 리소스(컴퓨트, 스토리지, 네트워크 등)를 바로바로 사용할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Broad network access: Access from anywhere&lt;/b&gt;&lt;br /&gt;인터넷만 연결되있다면 접속이 가능함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Resource pooling: Provider shares resources to customers&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Rapid elasticity: Get more resourcees quickly as needed&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Measured service: Pay only for what you consume&lt;/b&gt;&lt;br /&gt;항장 준비되어있고, 언제든지 고객의 필요에 따라 사용할 수 있는 거대한 리소스 풀을 제공하며, 고객은 물리적 리소스에 대한 걱정을 할 필요가 없음. 언제든지 확장 가능하며 사용하거나 예약한 만큼만 비용을 지불하고, 필요없어진다면 언제든지 삭제하고 비용지불을 멈출 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 잘 알려진 Virtual machine 부터 Kubernetes 까지 다양한 managed service를 이용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3vMzy/btrNxfinR9u/gQXO7JdQzgL5cv0rkK0Vnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3vMzy/btrNxfinR9u/gQXO7JdQzgL5cv0rkK0Vnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3vMzy/btrNxfinR9u/gQXO7JdQzgL5cv0rkK0Vnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3vMzy%2FbtrNxfinR9u%2FgQXO7JdQzgL5cv0rkK0Vnk%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;379&quot; height=&quot;305&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;580&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;Compute Engine : GCP에서 제공하는 On demand Virtual Machine 서비스.&lt;/li&gt;
&lt;li&gt;GKE, Google Ckubernetes Engine : GCP에서 제공하는 Managed Kubernetes.&lt;/li&gt;
&lt;li&gt;App Engine : GCP에서 제공하는 Fully Managed Platform as a Service Framework. 코드만 업로드 하면 인프라 걱정없이 바로 구동시킬 수 있다.&lt;/li&gt;
&lt;li&gt;Cloud Functions : Serverless execution environment, Functions as a Service. 함수로써 동작할 코드만 넣으면 동작시킬 수 있으며 실행 횟수에 따라 비용을 지불한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GKE는 Compute Engine 기반으로 설계되었다. GCP에서 데이터베이스를 이용하려면 Compute Engine으로 VM을 만들어서 직접 database를 설치하거나 또는 GKE에서 데이터베이스 컨테이너를 구동시킬 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XicE6/btrNv0Gdfbu/PcMjqmteO8jtQLjtGMIcg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XicE6/btrNv0Gdfbu/PcMjqmteO8jtQLjtGMIcg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XicE6/btrNv0Gdfbu/PcMjqmteO8jtQLjtGMIcg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXicE6%2FbtrNv0Gdfbu%2FPcMjqmteO8jtQLjtGMIcg1%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;214&quot; height=&quot;275&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법 외에도 GCP 제공하는 fully managed database와 storage service를 이용하는 방법도 있다. 이런 방법은 직접 서버, 데이터베이스를 구성하는 수고를 줄여주며 relational 또는 non-relational database를 포함한 다양한 형태의 데이터베이스, 서비스들을 이용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sg2dC/btrNw9oOA9c/hB6YZIMTqkhCMKIHEzWGak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sg2dC/btrNw9oOA9c/hB6YZIMTqkhCMKIHEzWGak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sg2dC/btrNw9oOA9c/hB6YZIMTqkhCMKIHEzWGak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsg2dC%2FbtrNw9oOA9c%2FhB6YZIMTqkhCMKIHEzWGak%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;380&quot; height=&quot;265&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 빅데이터, 머신러닝을 위한 다양한 서비스를 GCP에서 제공하며, 이런 서비스를 이용해 다양한 루틴 작업들을 줄일 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q4E6Z/btrNExhGEGd/bdBQuyMNkwQs1DMuVGT2kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q4E6Z/btrNExhGEGd/bdBQuyMNkwQs1DMuVGT2kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q4E6Z/btrNExhGEGd/bdBQuyMNkwQs1DMuVGT2kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ4E6Z%2FbtrNExhGEGd%2FbdBQuyMNkwQs1DMuVGT2kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;419&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Resource Management&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP에서 사용하는 다양한 서비스의 물리 장치(서버, 하드드라이버 등)는 전 세계의 구글 데이터센터에 펼쳐져있다. GCP는 multi-region, region, zone에 있는 리소스들을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;multi-region : 3개로 나누어진 GCP 영역. America, Europe, Asia Pacific 으로 나뉜다.&lt;/li&gt;
&lt;li&gt;region : 같은 대륙에서 지역적으로 나누어져있는 지역. 왕복 네트워크시간 95%가 ms 미만. ex) europe-west2&lt;/li&gt;
&lt;li&gt;zone : region 안에서 한 지역에 있는 장소. 한곳에 있는 데이터센터들이라고 볼 수 있음. ex) europe-west2-a/b/c&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP의 compute engine의 인스턴스는 특정 zone에 배치된다. 만약 해당 zone의 상태가 unavailable 상태가 되면 compute engine도 같이 unavailable 상태가 되며, compute engine을 기반으로하는 GKE도 똑같이 영향을 받는다. 이런 리소스를들을 배포할 때 여러 zone에 배치를 한다면 예상치 못한 사고에 내결함성을 가질 수 있게 된다. 전세계의 40%의 인터넷 트래픽을 담당(추정)하는 구글 네트워크는 전세계에 배치된 데이터센터간에 매우 빠른 속도와 짧은 지연시간을 지원하고, 구글의 edge caching network는 이러한 네트워크망을 기반으로 GCP의 서비스들을 매우 빠르게 서비스한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 GCP 서비스들은 Zonal resources(기본적으로 single zone에 배치되는)이다. zonal level, regional level, multi-regional level이 있다. GKE의 node는 zonal resource이며 GKE는 regional level이다. multi-regional level로는 HTTP load balancer, Virtual Private Cloud 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스들은 반드시 project에 속한다. project는 GCP에서 조직 구성의 기본 단위이며 리소스, 서비스, 빌링, API, 권한을 설정할 수 있다. 프로젝트들은 변경이 불가능한 고유의 아이디와 넘버를 가진다. 사용자가 지정한 라벨을 통해 프로젝트를 필터링할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트들은 folder에 속할 수 있으며 폴더는 또 다른 폴더로 들어갈 수 있다. 최상위 grouping 단계는 organization이다. 고유의 변경불가능한 id 값을 가지며 만약 기업이라면 기업의 전체 GCP의 policy를 설정하는 등을 할 수 있다. 이러한 정책들은 하이라키 구조로 하위 레벨 구성에 상속된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yLcmi/btrN6oli4zw/9UxY646cWqupxOuJuH7sV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yLcmi/btrN6oli4zw/9UxY646cWqupxOuJuH7sV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yLcmi/btrN6oli4zw/9UxY646cWqupxOuJuH7sV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyLcmi%2FbtrN6oli4zw%2F9UxY646cWqupxOuJuH7sV1%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;276&quot; height=&quot;239&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;784&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;906&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciYQRH/btrN40Fyw6V/KxQwhAo0OF7tmIVLbuzLyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciYQRH/btrN40Fyw6V/KxQwhAo0OF7tmIVLbuzLyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciYQRH/btrN40Fyw6V/KxQwhAo0OF7tmIVLbuzLyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciYQRH%2FbtrN40Fyw6V%2FKxQwhAo0OF7tmIVLbuzLyK%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;239&quot; height=&quot;260&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌링은 프로젝트 레벨에서 측정될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF8KMG/btrOdhSY5lq/4xvH4segHfkbPJqKJUXQV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF8KMG/btrOdhSY5lq/4xvH4segHfkbPJqKJUXQV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF8KMG/btrOdhSY5lq/4xvH4segHfkbPJqKJUXQV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF8KMG%2FbtrOdhSY5lq%2F4xvH4segHfkbPJqKJUXQV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;294&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Billing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌링은 프로젝트 단위로 누적되며, 프로젝트 설정 시 billing account를 설정해야 한다. 빌링 계정은 결제 정보등 결제와 관련된 모든 정보를 담고있다. 하나 또는 다수의 프로젝트를 빌링 계정에 연결할 수 있다. 매달 또는 임계치에 다다를 때마다 자동으로 청구된다. subaccount를 만들어서 프로젝트별로 결제를 담당할 수 있다. 예를들어 GCP의 리소스를 만들어서 다른 여러 회사들에 판매할 때 활용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌링 사고를 방지하기 위해 3가지 무료 빌링 서비스를 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Budgets and alerts&lt;br /&gt;프로젝트 또는 빌링 계정 단위로 budget을 설정하고 설정한 budget에 대한 알람을 설정할 수 있다.&lt;br /&gt;예를들어 100원의 예산을 설정하고 90%에 대한 알람을 설정하면 90원이 되었을 때 메일로 알람이 오도록 설정할 수 있으며 webhook을 설정해 resource를 삭제하는 등의 트리거도 설정할 수있다.&lt;/li&gt;
&lt;li&gt;Billing export&lt;br /&gt;세부 사항을 출력할 수 있으며 big query 등을 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;Reports&lt;br /&gt;프로젝트 또는 서비스별 빌링 상황을 시각화하여 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quotas(할당량)으로 제어할 수 도 있다. 할당량의 종류는 아래와 같은 타입이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Rate Quota&lt;br /&gt;GKE API : 100초당 1000번의 리퀘스트만 받음&lt;/li&gt;
&lt;li&gt;Allocation Quota&lt;br /&gt;프로젝트당 5개의 private network 만 허용&lt;/li&gt;
&lt;li&gt;각각의 서비스별로 다양한 quota를 설정할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Interacting with Google Cloud&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IDOMv/btrN4Z7E37t/WTgQKuJiwzpemJxGEW7gkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IDOMv/btrN4Z7E37t/WTgQKuJiwzpemJxGEW7gkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IDOMv/btrN4Z7E37t/WTgQKuJiwzpemJxGEW7gkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIDOMv%2FbtrN4Z7E37t%2FWTgQKuJiwzpemJxGEW7gkk%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;221&quot; height=&quot;251&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;958&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;Google Cloud Console&lt;br /&gt;Web-based GUI 제공. 프로젝트와 리소스들을 시각화하고 마우스 클릭으로 작업들을 실행할 수 있음&lt;/li&gt;
&lt;li&gt;Cloud SDK&lt;br /&gt;gcloud, kubectl, gsutil, bq 등 툴을 포함하고있는 cloud sdk를 머신에 설치해서 사용가능. 스크립트로 이런 명령어를 사용해서 자동화할 수 있음.&lt;/li&gt;
&lt;li&gt;Cloud Shell&lt;br /&gt;브라우저에서 gcp resource에 CLI 기반으로 접근 가능한 쉘 제공. Cloud SDK가 이미 설치되어 있어 사용 가능. 임시 VM을 사용한다. 이 임시 VM은 유저별로 하나씩 제공되긴 하지만 쉘을 멈추면 VM도 멈추고 쉘을 시작하면 VM도 다시 시작되므로 프로덕션용으로 사용할 수 없다. 또한 5GB의 persistence storage를 가질 수 있다. 또한 editor 도 있어 간편하게 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;Cloud Console mobile app&lt;/li&gt;
&lt;li&gt;REST-based API&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Containers and Container Images&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4enJ2/btrOdgs6yfb/fZgxMa5kKbW9WjKVmDxXB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4enJ2/btrOdgs6yfb/fZgxMa5kKbW9WjKVmDxXB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4enJ2/btrOdgs6yfb/fZgxMa5kKbW9WjKVmDxXB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4enJ2%2FbtrOdgs6yfb%2FfZgxMa5kKbW9WjKVmDxXB0%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;346&quot; height=&quot;355&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;896&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;Dedicated server&lt;br /&gt;기본적으로 애플리케이션을 배포하기 위해선 적절한 물리적인 장소를 찾고, 파워, 쿨링, 네트워크 등 하드웨어 장비를 준비해서 Kernel, Dependencies, Application Code를 set up하는 과정을 진행했어야 했음.&lt;br /&gt;만약 애플리케이션을 운영하면서 확장이 필요할 경우 똑같은 과정을 반복하면됨. 이러한 과정은 시간, 비용이 많이 소요됨. 애플리케이션은 OS나 하드웨어에 종속적이였음.&lt;/li&gt;
&lt;li&gt;Virtual machine&lt;br /&gt;virtualization 기술의 Hypervisor는 물리적인 서버들의 하드웨어를 가상화해 여러 가상 서버를 실행할 수 있게 하여 애플리케이션을 하드웨어 종속성으로부터 독립시킴. KVM은 대표적인 하이퍼바이저 중 하나임. 서버가 필요할 경우 하이퍼바이저에서 가상서버를 생성하기만 하면 되어 이전 방식보다 획기적으로 비용, 시간이 감소됨. 또한 가상머신 이미지등을 사용해서 쉽고 빠르게 복제본을 생성할 수 있게됨. 이 방식의 단점은 애플리케이션은 여전히 OS의 종속성을 가지고있었고, 서비스 시작을 위해선 가상머신의 boot time이 필요했음. 또한 하나의 머신에 여러 서비스를 구동할 경우 종속성의 문제가 있을 수 있으며 하나의 애플리케이션 문제가 머신에 영향을 미치면 다른 애플리케이션도 영향을 받게 되었음.&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;898&quot; data-origin-height=&quot;796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kS2kX/btrN4xczu3l/YOe0sXSvKCA9AJ9R1XsTP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kS2kX/btrN4xczu3l/YOe0sXSvKCA9AJ9R1XsTP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kS2kX/btrN4xczu3l/YOe0sXSvKCA9AJ9R1XsTP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkS2kX%2FbtrN4xczu3l%2FYOe0sXSvKCA9AJ9R1XsTP0%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;311&quot; height=&quot;276&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 나은 방식으로, 애플리케이션, dependency 레벨에서 추상화를 구현하여, 이전 방식 처럼 전체 하드웨어, 운영체제를 가상화할 필요 없이 user space만 가상화하면 됨. user space는 커널 위에 있으며 애플리케이션 코드와 dependency가 위치한 공간이며 이것이 container임. 컨테이너는 하드웨어, OS를 가상화하지 않기 때문에 가벼우며 boot time이 없음. 또한 컨테이너간 별도의 user space를 가지기 때문에 서로 독립적이며 같은 kernel 위에서 portable함. 시스템 환경/라이브러리 등 여러 환경에 독립적으로 동작할 수 있음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R3V9N/btrN4PD2roP/GEhSP1S3v5njhU2GFkGVx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R3V9N/btrN4PD2roP/GEhSP1S3v5njhU2GFkGVx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R3V9N/btrN4PD2roP/GEhSP1S3v5njhU2GFkGVx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR3V9N%2FbtrN4PD2roP%2FGEhSP1S3v5njhU2GFkGVx0%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;228&quot; height=&quot;728&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 커널이라면 랩탑, 서버, 가상머신 등의 환경에 영향을 받지않고 언제든 실행을 보장할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 코드와 dependency의 묶음을 image라고하고 컨테이너는 이미지를 running한 인스턴스이다. 개발자는 애플리케이션 코드를 이미지로 빌드해서 portable package를 만들 수 있다. 이미지를 빌드하고 컨테이너를 구동하기 위해선 tool이 필요한데, 오픈소스 프로젝트인 docker가 이 두가지를 모두 제공한다. 하지만 쿠버넷이 하는 것 처럼 컨테이너들을 orchestration하진 않음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNCBzE/btrN499J4YO/nLBeCnrJBPK5XCoLkWvV1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNCBzE/btrN499J4YO/nLBeCnrJBPK5XCoLkWvV1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNCBzE/btrN499J4YO/nLBeCnrJBPK5XCoLkWvV1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNCBzE%2FbtrN499J4YO%2FnLBeCnrJBPK5XCoLkWvV1k%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;329&quot; height=&quot;199&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;Processes : 리눅스 프로세스는 다른 프로세스와 분리된 각각의 virtual memory address space를 가지며, 빠르게 create/destory 됨&lt;/li&gt;
&lt;li&gt;namespace : process id number, directory tree, ip 등을 사용하기 위해 사용&lt;/li&gt;
&lt;li&gt;cgroups : 컨테이너의 자원(cpu, mem 등)을 컨트롤하기 위해 사용&lt;/li&gt;
&lt;li&gt;union file system : code, dependecy로 layer를 구성하기 위해 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;874&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2zp95/btrN4Z1a7QP/IKi7Aq5ZueXYjEc0BGncd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2zp95/btrN4Z1a7QP/IKi7Aq5ZueXYjEc0BGncd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2zp95/btrN4Z1a7QP/IKi7Aq5ZueXYjEc0BGncd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2zp95%2FbtrN4Z1a7QP%2FIKi7Aq5ZueXYjEc0BGncd1%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;328&quot; height=&quot;874&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;874&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 layer로 구성되어있고 이미지 빌드 도구는 이미지를 빌드하기 위해 container manifest를 참조하는데, docker의 경우 Docker file 이다. Docker file은 이미지의 레이어 정보를 가지고 있는데, 이 레이어들은 read-only이며 컨테이너가 구동되면 맨 마지막에 w/r이 가능한 ephemeral layer가 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddilpu/btrOgxOXWkg/1tNy570qhBSLb4bKnQljVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddilpu/btrOgxOXWkg/1tNy570qhBSLb4bKnQljVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddilpu/btrOgxOXWkg/1tNy570qhBSLb4bKnQljVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddilpu%2FbtrOgxOXWkg%2F1tNy570qhBSLb4bKnQljVK%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;900&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 도커파일의 FROM, COPY, RUN, CMD 명령어는 실행되면서 각각 하나의 layer를 차례차례 만들며 각 레이어는 이전 레이어에서의 변경사항만 가지고 있다. 도커파일을 작성할 때 변동성이 가장 낮은 레이어를 맨 아래(맨 처음)에 두고 차례차례 두는 것이 더 효율적이다.&lt;br /&gt;이미지가 컨테이너로써 실행되면 conatiner layer가 생성되어 해당 레이어는 컨테이너 러닝동안 R/W가 가능하며 컨테이너가 종료되면 사라진다. 즉 컨테이너에서 작업한 데이터를 보존하기 위해선 다른 persistence storage를 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조에서 하나의 이미지를 사용하는 여러 컨테이너들은 개별 container layer를 가지고 있으면서 그 아래에 동일한 base image layer를 가지고 있으므로 효율적으로 활용될 수 있음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqHU6w/btrN67xuKgo/3HvEaFcFO4LGsQ8ikXjrH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqHU6w/btrN67xuKgo/3HvEaFcFO4LGsQ8ikXjrH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqHU6w/btrN67xuKgo/3HvEaFcFO4LGsQ8ikXjrH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqHU6w%2FbtrN67xuKgo%2F3HvEaFcFO4LGsQ8ikXjrH1%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;424&quot; height=&quot;300&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 컨테이너를 빌드할 때, 전체 이미지를 복사하는 것이 아니라 변경점이 있는 레이어부터 만든다. 위 그림을 보면 여러개의 컨테이너가 같은 base image(read only)인 ubuntu:18.04 이미지를 공유 하면서, 그 위에 각각의 R/W layer를 가지고 있는 것을 볼 수 있다. 이것이 VM보다 더 빠르게 동작할 수 있는 이유 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP에선 image 관련 환경으로 container registyr인 gcr.io와 이미지 빌드 자동화 도구인 Cloud Build를 제공한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Introduction to Kubernetes&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버넷은 on-premise 또는 Cloud에서 컨테이너를 컨트롤할 수 있는 오픈소스 플랫폼이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w2UZ5/btrOeRfX2Ut/EQzwzSjtOappdKWk2mu42K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w2UZ5/btrOeRfX2Ut/EQzwzSjtOappdKWk2mu42K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w2UZ5/btrOeRfX2Ut/EQzwzSjtOappdKWk2mu42K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw2UZ5%2FbtrOeRfX2Ut%2FEQzwzSjtOappdKWk2mu42K%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;289&quot; height=&quot;168&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;710&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;open source : 벤더에 종속적이지 않은 Cloud Native Computing Foundation 재단의 오픈소스 프로젝트&lt;/li&gt;
&lt;li&gt;Automation :&amp;nbsp; deployment, scaling, load balancing, logging, monitoring 등 다양한 management features를 자동화&lt;/li&gt;
&lt;li&gt;Declarative configuration : 관리자는 복잡한 설정을 이해할 필요 없이 desired state를 정의하고 kuberentes가 이를 달성하기 위한 동작을 수행&lt;/li&gt;
&lt;li&gt;Imperative configuration : 명령형 설정도 지원함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceP1yH/btrOc8WGT8d/JDFUMkzGkAUvuCJsbzLSR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceP1yH/btrOc8WGT8d/JDFUMkzGkAUvuCJsbzLSR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceP1yH/btrOc8WGT8d/JDFUMkzGkAUvuCJsbzLSR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceP1yH%2FbtrOc8WGT8d%2FJDFUMkzGkAUvuCJsbzLSR0%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;245&quot; height=&quot;910&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;stateless, stateful application 뿐만 아니라 batched job, deamon tasks 등 다양한 application workload를 지원&lt;/li&gt;
&lt;li&gt;애플리케이션의 사용량에 따라 auto scale in-out을 지원&lt;/li&gt;
&lt;li&gt;리소스의 사용량을 제어할 수 있음&lt;/li&gt;
&lt;li&gt;다양한 플러그인, 확장을 지원함&lt;/li&gt;
&lt;li&gt;오픈소스이기때문에 vendor lock in 없이 다양한 환경에 자유롭게 배포, 이동이 가능함&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Introduction to Google Kubernetes Engine&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP에선 관리형(fully managed) 쿠버넷 서비스로 GKE를 서비스함.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RmCFN/btrN5v6GFzH/SDvLysfAJhIRjPEIz6nLy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RmCFN/btrN5v6GFzH/SDvLysfAJhIRjPEIz6nLy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RmCFN/btrN5v6GFzH/SDvLysfAJhIRjPEIz6nLy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRmCFN%2FbtrN5v6GFzH%2FSDvLysfAJhIRjPEIz6nLy1%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;297&quot; height=&quot;328&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;890&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;GKE 장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GKE에서 fully managed를 해줌. 노드를 생성하기 위해 vm을 provision 하거나 등의 작업들을 자동화된 프로세스로 지원함&lt;/li&gt;
&lt;li&gt;컨테이너에 최적화된 OS로 GKE를 구동하여 쉽게 확장 가능&lt;/li&gt;
&lt;li&gt;자동으로 버전을 업그레이드할 수 있게 해줌&lt;/li&gt;
&lt;li&gt;노드가 탈락되었을 때 자동으로 복구해주는 등을 해줌&lt;/li&gt;
&lt;li&gt;클러스터를 자동화된 프로세스로 scale in-out 할수 있음&lt;/li&gt;
&lt;li&gt;GCP의 다른 container 관련 서비스(registry, Cloud Build) 등과 통합되어 있음&lt;/li&gt;
&lt;li&gt;GCP의 IAM과 통합 가능&lt;/li&gt;
&lt;li&gt;GCP의 로깅&amp;amp;모니터링 서비스와 통합 가능&lt;/li&gt;
&lt;li&gt;기존 GCP의 네트워크와 통합 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Computing Options Detail&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr4wb5/btrOeRf8ghr/RguwKYEGrEKnsVx1AkC08k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr4wb5/btrOeRf8ghr/RguwKYEGrEKnsVx1AkC08k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr4wb5/btrOeRf8ghr/RguwKYEGrEKnsVx1AkC08k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr4wb5%2FbtrOeRf8ghr%2FRguwKYEGrEKnsVx1AkC08k%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;544&quot; height=&quot;260&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP에서 compute workload를 구동하기 위해 제공되는 서비스와 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Compute Engine&lt;br /&gt;GCP에서 제공하는 virtual machine service&lt;br /&gt;- infrastructure, OS에 대한 customizing을 원할 때 사용&lt;br /&gt;- 운영체제를 섞어서 애플리케이션을 구동시키고 싶을 때 사용&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fully customizable virtual machines : GCP에서 제공하는 가상머신 서비스. 자유롭게 설정한 가상머신을 제공받음. 현재 160 vCPUs, 3 TB memory 이상을 신청할 수 있음.&lt;/li&gt;
&lt;li&gt;Persistenct disks and optional local SSDs : persistence storage를 위한 두 가지 옵션 제공. 64TB 이상의 network storage를 제공하며 스냅샷 등 기능 이용 가능. 빠른 I/O를 위한 local SSD도 사용 가능.&lt;/li&gt;
&lt;li&gt;Glocal load balancing and autoscaling : managed instance group 설정을 통해 global load balancer를 활용하여 auto-scaling을 설정할 수 있음&lt;/li&gt;
&lt;li&gt;Per-second billing : 배치잡 등에 유용하게 활용할 수 있도록 초당 청구됨. preemptible vm으로 매우 저렴한 가격에 서비스를 이용할 수도 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;App Engine&lt;br /&gt;fully managed application platform. 별도의 서버 설정, 배포 설정 없이 코드만으로 서비스를 구동&lt;br /&gt;- 서버, 배포에 대한 걱정없이 코드만 작성해서 배포하고싶을 때 사용&lt;br /&gt;- mobile app, web, gaming backend, RESTful API 등에 활용&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;provides a fully managed, code-first platform&lt;/li&gt;
&lt;li&gt;Streamlines application deployment and scalability&lt;/li&gt;
&lt;li&gt;Provides support for popular programming languages and application runtimes&lt;/li&gt;
&lt;li&gt;Supports integrated monitoring, logging, and diagnostics&lt;/li&gt;
&lt;li&gt;Simplifies version control, canary testing, and rollback&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GKE
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fully managed Kubernetes platform&lt;/li&gt;
&lt;li&gt;Supports cluster scaling, persistence disks, automated upgrades and auto node repairs&lt;/li&gt;
&lt;li&gt;Built-in integration with Google Cloud Service&lt;/li&gt;
&lt;li&gt;Portability across multiple environment(Hibrid computing, Multi cloud computing)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cloud Run&lt;br /&gt;managed compute platform&lt;br /&gt;- 특정 이벤트가 발생했을 때 stateless container를 실행하고 싶을 때&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enables stateless containers&lt;/li&gt;
&lt;li&gt;Abstract away infrastructure management&lt;/li&gt;
&lt;li&gt;Automatically scales up and down&lt;/li&gt;
&lt;li&gt;Open API and runtime environment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cloud Functions&lt;br /&gt;for simple, single-purpose functions&lt;br /&gt;python, go, javscript로 쓰여진 코드만 업로드하면 scaling, HA, fault-tolerant를 자동으로 구성할 수 있음&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Event-driven, serverless conmpute service&lt;/li&gt;
&lt;li&gt;Automatic scaling with highly available and fault-tolerant design&lt;/li&gt;
&lt;li&gt;Charges apply only when your code runs&lt;/li&gt;
&lt;li&gt;Triggered based on events in Google Cloud services, HTTP endpoints, and Firebase&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;1700&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnWRTX/btrOgxhtgX9/YhZQoGmZlDVxu7vdQWkcdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnWRTX/btrOgxhtgX9/YhZQoGmZlDVxu7vdQWkcdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnWRTX/btrOgxhtgX9/YhZQoGmZlDVxu7vdQWkcdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnWRTX%2FbtrOgxhtgX9%2FYhZQoGmZlDVxu7vdQWkcdk%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;569&quot; height=&quot;294&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kubernetes Concepts&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버넷의 두 가지 중요한 메인 컨셉은 object model과 declarative management임.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TGziS/btrPhokyONs/MotjpLwPhpIZHVnj87uWXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TGziS/btrPhokyONs/MotjpLwPhpIZHVnj87uWXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TGziS/btrPhokyONs/MotjpLwPhpIZHVnj87uWXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTGziS%2FbtrPhokyONs%2FMotjpLwPhpIZHVnj87uWXk%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;411&quot; height=&quot;888&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 object model의 attribute를 수정해서 state를 변경시킬 수 있음. 이 변경은 declarative 방식으로 되는데 사용자는 쿠버넷이 달성하길 원하는 오브젝트와 오브젝트의 상태를 정의하고 쿠버넷은 사용자가 정의한 상태를 달성하기 위해 동작함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버넷에서의 오브젝트는 두 가지 상태를 가지고있음. 사용자가 정의한 상태(desired state), 실제로 클러스터에 구동중인 현재 오브젝트의 상태(current state). object spec은 사용자가 정의한 desired state가 기록된 element이고, object status는 쿠버넷의 control plane이 기록하는 현재 오브젝트의 상태를 기록하는 element.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 controle plane이란 쿠버넷 클러스터의 프로세싱을 담당하는 쿠버넷 구성요소 중 하나.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyeYi1/btrPmEsYL2i/Y0zrUDDNnaKdEcXUyu6x21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyeYi1/btrPmEsYL2i/Y0zrUDDNnaKdEcXUyu6x21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyeYi1/btrPmEsYL2i/Y0zrUDDNnaKdEcXUyu6x21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyeYi1%2FbtrPmEsYL2i%2FY0zrUDDNnaKdEcXUyu6x21%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;309&quot; height=&quot;158&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버넷의 basic building block, smallest deployable object. 컨테이너는 쿠버넷에서 pod으로 구동됨. 하나의 팟은 여러개의 컨테이너로 구성되어있으며 쿠버넷은 pod에게 고유의 ip를 부여하고 pod의 내부 컨테이너는 127.0.0.1로 통신함. 팟의 컨테이너들은 network, storage를 공유함.&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;1560&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MxfjH/btrPkrgzeXg/hWPXhdBGLOXFDJSAFHydd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MxfjH/btrPkrgzeXg/hWPXhdBGLOXFDJSAFHydd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MxfjH/btrPkrgzeXg/hWPXhdBGLOXFDJSAFHydd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMxfjH%2FbtrPkrgzeXg%2FhWPXhdBGLOXFDJSAFHydd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;165&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 컨테이너를 3개 구동시키고 싶다면, 사용자는 nginx 컨테이너에 대한 object 정보를 기입하면 쿠버넷은 사용자가 작성한 disire state를 current state로 만들기 위해 작업한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOsP1/btrPiuYXk6E/WfF4jqUS6FHX0mwpUv5E3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOsP1/btrPiuYXk6E/WfF4jqUS6FHX0mwpUv5E3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOsP1/btrPiuYXk6E/WfF4jqUS6FHX0mwpUv5E3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOsP1%2FbtrPiuYXk6E%2FWfF4jqUS6FHX0mwpUv5E3k%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;248&quot; height=&quot;192&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용자가 입력한 desired state와 current state를 비교해서 쿠버넷 컨트롤 플레인이 desired state가 current state와 일치할 때 까지 반복적으로 계속 시도한다. 이런 쿠버넷의 행위를 watch loop라고 한다. 이런 작업은 첫 생성 이후부터 endless로 계속 모니터링되며 desired state와 current state를 일치시키기 위해 동작한다.&lt;br /&gt;&lt;br /&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;The Kubernetes Control Plane&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7xrWr/btrPlHqbqN8/6CATtB6HJTMfe2xATDfKsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7xrWr/btrPlHqbqN8/6CATtB6HJTMfe2xATDfKsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7xrWr/btrPlHqbqN8/6CATtB6HJTMfe2xATDfKsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7xrWr%2FbtrPlHqbqN8%2F6CATtB6HJTMfe2xATDfKsK%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;499&quot; height=&quot;269&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;902&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버넷 클러스터를 구성하기 위해선  컴퓨터가 필요하다. 클라우드를 포함한 대부분의 쿠버넷 클러스터들은 가상머신을 노드로 사용한다. 클러스터 중 하나의 컴퓨터를 controle plane이라고 부르고 나머지를 nodes라고 부른다.&amp;nbsp; nodes는 pod의 실행을 담당하고, 컨트롤 플레인은 클러스터의 coordinate를 담당한다. 클러스테에서 중요한 부분을 담당하는 컴포넌트들은 컨트롤 플레인에 배치된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kube-API server : 클러스터를 컨트롤할 수 있는 기능들을 API로 제공하며 클러스터를 조회하거나 변경하는 모든 쿼리들이 api server를 통해야 한다. 대표적으로 kubectl이 api server를 통해 클러스터와 통신한다.&lt;/li&gt;
&lt;li&gt;etcd : 쿠버넷 클러스터에 대한 정보 저장소이다. 현재 클러스터/노드 구성 정보, 팟이 어느 노드에서 구동중인지 등의 클러스터 정보를 저장한다. etcd를 direct로 사용해선 절대 안되며 api server로 접근해야 한다.&lt;/li&gt;
&lt;li&gt;kube-scheduler : 팟의 스케쥴링을 담당한다. 설정 정보를 읽어 팟을 적절한 노드에 배정한다.&lt;/li&gt;
&lt;li&gt;kube-controller-manager : kube-api-server를 통해 클러스터를 모니터링하며 desired state와 current state를 맞추는 역할을 수행한다.&lt;/li&gt;
&lt;li&gt;kube-cloud-manager : 쿠버넷 클러스터와 클라우드 프로바이더와 통신하며 여러가지 필요 서비스를 제공하기 위해 사용된다.&lt;/li&gt;
&lt;li&gt;kubelet : 쿠버넷 클러스터가 각 노드에 설치하는 agent. container runtime을 가지고 있으면서 pod을 구동하고 라이프사이클을 모니터링하면서 api server와 통신한다. 쿠버넷은 여러 컨테이너 런타임을 지원하지만 GKE는 containerd를 사용한다.&lt;/li&gt;
&lt;li&gt;kube-proxy : 노드들에 구동중인 팟들의 네트워크를 제어하기 위한 컴포넌트. 오픈소스 쿠버넷은 iptables의 firewall을 사용해 해당 기능을 제공한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Cloud</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/72</guid>
      <comments>https://rura6502.tistory.com/entry/Google-Cloud-StudyJam-CourseraArchitecting-with-Google-Kubernetes-Engine-FoundationsWorloadProductions#entry72comment</comments>
      <pubDate>Sun, 2 Oct 2022 18:19:46 +0900</pubDate>
    </item>
    <item>
      <title>디자인 패턴 cheat sheet</title>
      <link>https://rura6502.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-cheat-sheet</link>
      <description>&lt;h1&gt;팩토리 패턴&lt;/h1&gt;
&lt;p&gt;객체를 사용하는 측에서 바로 생성하지 않고 중간에 생성을 전담하는 객체를 두고 생성하여 결합도를 낮춘다.&lt;/p&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;동물 카페(AnimalCafe)에서 동물을 만날 수 있는데 현재는 토끼만 만날 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Animal { /* ... */ }
class Rabbit implements Animal { /* ... */ }

class AnimalCafe {
  public Animal meet() {
      Animal animal = new Rabbit();
    // 기타 로직(등록, 지불 등)
    return animal;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 코드에서 &lt;code&gt;meet()&lt;/code&gt; 클래스는 고객에게 제공할 동물을 선택하고, 제공하기 위해서 &lt;code&gt;기타 로직&lt;/code&gt;을 수행한다. 이후 추가 케이스로 강아지(Puppy)가 추가되었고 카페에선 오전엔 토끼, 오후엔 강아지를 제공하려고 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Puppy implements Animal { /* ... */ }

class AnimalCafe {
  public Animal meet() {
    Animal animal;
      if (지금시간 == 오전)
      animal = new Rabbit();
    else
      animal = new Puppy();
    // 기타 로직(등록, 지불 등)
    return animall;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또 다른 동물 종류가 추가되면 &lt;code&gt;meet()&lt;/code&gt; 메소드의 수정이 지속적으로 필요하다. &lt;code&gt;meet()&lt;/code&gt; 메소드는 &lt;code&gt;조건에 따라서 제공할 동물을 선택하는 작업&lt;/code&gt;과 &lt;code&gt;고객에게 동물을 제공하기 위한 작업&lt;/code&gt; 이 두 가지 작업에 대한 책임을 가지고 있다. 두 가지 작업 중 하나라도 변경이 필요하면 &lt;code&gt;meet()&lt;/code&gt; 메소드는 수정되어야한다. &lt;code&gt;meet()&lt;/code&gt; 메소드가 하나의 책임만을 가지도록 변경하기 위해 &lt;code&gt;조건에 따라서 제공할 동물을 선택하는 작업&lt;/code&gt;을 아래처럼 분리할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AnimalFactory {
  public static Animal pick() {
    if (지금시간 == 오전)
      return Rabbit();
    else
      return new Puppy();
  }
}

class AnimalCafe {
  public Animal meet() {
    Animal animal = AnimalFactory.pick();
    return animall;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;AnimalCafe&lt;/code&gt; 클래스에서 조건에 따라 동물을 생성하는 로직을 &lt;code&gt;AnimalFactory&lt;/code&gt; 클래스의 &lt;code&gt;pick()&lt;/code&gt; 메소드로 분리하였다. 이로써 &lt;code&gt;AnimalCafe#meet()&lt;/code&gt; 메소드의 &lt;code&gt;고객에게 동물을 제공하기 위한 작업&lt;/code&gt;에 대한 책임만을 가지게 되었다.&lt;/p&gt;
&lt;h3&gt;싱글톤 패턴 활용&lt;/h3&gt;
&lt;p&gt;상황에 따라서 매번 인스턴스를 생성할 필요가 없을 경우 싱글톤 패턴 적용을 아래와 같이 고려할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Pupply implements Animal() {
  private Animal puppy;

  public static Animal getInstance() {
    if (puppy == null)
      puppy = new Puppy();

    return puppy;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;템플릿 메서드 패턴 활용&lt;/h3&gt;
&lt;p&gt;// TODO&lt;/p&gt;
&lt;h1&gt;싱글톤 패턴&lt;/h1&gt;
&lt;p&gt;클라이언트가 사용하려는 객체가 클라이언트의 라이프 사이클 동안 단 하나의 인스턴스임을 보장할 수 있는 패턴. 아래와 같은 상황에서 활용될 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공유 자원 접근&lt;/li&gt;
&lt;li&gt;복수의 시스템이 하나의 자원에 접근할 때&lt;/li&gt;
&lt;li&gt;유일한 객체가 필요할 때&lt;/li&gt;
&lt;li&gt;값의 캐시가 필요할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;모든 사람이 사용할 수 있는 공용 자원인 단 한대의 TV&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Tv {
  private static Tv tv;

  private int nowChannelNo = 0;

  private Tv tv() {}

  public static Tv getInstance() {
    if (tv == null)
      tv = new Tv();

    return tv;
  }

  public void changeChannel(int channerNo) {
    this.nowChannelNo = channerNo;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;멀티쓰레드 환경에서의 싱글톤&lt;/h2&gt;
&lt;p&gt;여러 쓰레드가 위 앞선 예제의 &lt;code&gt;getInstance()&lt;/code&gt; 메소드에 접근할 경우 &lt;code&gt;if (singletone == null)&lt;/code&gt; 접근 시점에 따라 여러 &lt;code&gt;Singletone&lt;/code&gt; 객체를 생성할 수 있다. 이를 방지하기 위해 &lt;code&gt;바로 초기화 하는 방법&lt;/code&gt;과 &lt;code&gt;동기화&lt;/code&gt; 방식이 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 바로 초기화 하는 방식
class Tv {
  private static Tv tv = new Tv();;

  public static Tv getInstance() {
    return tv;
  }


}

// 동기화(synchronized 사용)
class Tv {
  private static Tv tv;

  public synchronized static Tv getInstance() {
    if (tv == null)
      tv = new Tv();

    return tv;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;싱글톤 vs Statis Method&lt;/h2&gt;
&lt;p&gt;정적 변수와 메소드들로만 이루어진 정적 클래스를 구성하면 싱글톤 패턴의 클래스와 같은 역할을 할 수 있다. 하지만 인터페이스를 구현해야하는 경우 정적 메소드를 사용할 수 없다. 위의 예제의 경우 Tv를 사용할 때 &lt;code&gt;interface Tv&lt;/code&gt;를 구현한 &lt;code&gt;TvA&lt;/code&gt;, &lt;code&gt;TvB&lt;/code&gt;가 있다고 할 때, 클라이언트에서 &lt;code&gt;Tv&lt;/code&gt;를 참조하고있고, 경우에 따라 &lt;code&gt;TvA&lt;/code&gt;의 인스턴스, &lt;code&gt;TvB&lt;/code&gt;의 인스턴스를 사용한다고 하면 이는 정적 클래스로 구현이 불가능하다.&lt;/p&gt;
&lt;h1&gt;전략 패턴&lt;/h1&gt;
&lt;p&gt;객체의 행위를 캡슐화하여 분리한다.&lt;/p&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;로봇은 움직일 수 있고 공격할 수 있다. 각 로봇의 타입에 따라서 움직이거나 공격하는 방식이 다르다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Robot {
  void move();
  void attack();
}

class RobotA implements Robot {
  void move() { do(&amp;quot;로봇 A가 걸어갑니다&amp;quot;) }
  void attack() { do(&amp;quot;로봇 A가 총을 사용합니다.&amp;quot;) }
}

class RobotB implements Robot {
  void move() { do(&amp;quot;로봇 B가 뛰어갑니다.&amp;quot;) }
  void attack() { do(&amp;quot;로봇 B가 화염방사기를 사용합니다.&amp;quot;) }
}

class Client {
  main() {
    RobotA a = new RobotA();
    RobotB b = new RobotB();
    a.move()
    a.attack()
    b.move()
    b.attack()
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;로봇 A가 날아가게 하려면? =&amp;gt; 아래와 같은 기존 코드 변경이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class RobotA implements Robot { void move() { // do(&amp;quot;로봇 A가 걸어갑니다&amp;quot;) do(&amp;quot;로봇 A가 날아갑니다&amp;quot;) } ... }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;로봇C를 추가하고 B와 똑같은 방식으로 동작하게하려면? 화염방사기가 고장나서 물대포 방사기로 변경하여야된다면? =&amp;gt; 두 곳의 수정이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class RobotB implements Robot { ... void attack() { do(&amp;quot;로봇 C가 물대포 방사기를 사용합니다.&amp;quot;) } } class RobotB implemenets Robot { ... void attack() { // do(&amp;quot;로봇 B가 화염 방사기를 사용합니다.&amp;quot;) do(&amp;quot;로봇 B가 물대포 방사기를 사용합니다.&amp;quot;) } } class RobotB implemenets Robot { void move() { do(&amp;quot;로봇 C가 뛰어갑니다.&amp;quot;) } void attack() { // do(&amp;quot;로봇 B가 화염 방사기를 사용합니다.&amp;quot;) do(&amp;quot;로봇 C가 물대포 방사기를 사용합니다.&amp;quot;) } }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;수행하는 행위를 캡슐화하여 외부로 추출하여 사용할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface MoveStrategy { void move(); }
class WalkMoveStrategy implements MoveStrategy { void move() { do(&amp;quot;걷는다&amp;quot;) }
class FlyMoveStrategy implements MoveStrategy { void move() { do(&amp;quot;난다&amp;quot;) }

interface AttackStrategy { void attack(); }
class GunAttackStrategy implements AttackStrategy { void attack() { do(&amp;quot;총 공격&amp;quot;) }
class FireAttackStrategy implements AttackStrategy { void attack() { do(&amp;quot;화염방사기 공격&amp;quot;) }

interface Robot {
  void move(MoveStrategy moveStrategy);
  void attack(AttackStrategy attackStrategy);
}

class RobotA implements Robot {

  public RobotA(MoveStrategy moveStrategy, AttackStragegy attackStrategy) {
    setMoveStrategy(moveStrategy)
    setAttackStrategy(attackStrategy)
  }

  void move() { moveStrategy.move() }
  void attack() { attackStrategy.attack() }
}

class RobotB implements Robot {

  public RobotA(MoveStrategy moveStrategy, AttackStragegy attackStrategy) {
    setMoveStrategy(moveStrategy)
    setAttackStrategy(attackStrategy)
  }

  void move() { moveStrategy.move() }
  void attack() { attackStrategy.attack() }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;MoveStrategy&lt;/code&gt;, &lt;code&gt;AttackStrategy&lt;/code&gt;만 변경하여 로봇에 주입함으로써 로봇의 움직임, 공격 방식을 수정하거나 기존에 만들었던 전략을 활용 가능함으로써 재활용도 가능해졌다.&lt;/p&gt;
&lt;h1&gt;상태 패턴&lt;/h1&gt;
&lt;p&gt;객체가 상태(객체가 가질 수 있는 어떤 조건이나 상황)가 변함에 따라 수행할 수 있는 행위를 외부로 분리하여 상태의 변경에 유연하게 대처할 수 있도록 한다.&lt;/p&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;OFF, 약풍이 있는 선풍기 객체를 설계한다. 여기서 OFF, 약풍은 선풍기 객체의 상태에 해당한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Fan {
  private static int OFF = 0;
  private static int 약풍 = 1;
  private int 현재상태 = OFF;

  ...
  public push(int signal) {
    if (signal == OFF)
      끈다(); 현재상태=OFF;
    else if (signal == 약풍)
      약풍(); 현재상태=약풍;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;여기서 강풍이 추가된다면 아래의 변경이 필요하다. =&amp;gt; 새로운 상태가 추가될 때마다 그 상태를 처리하기 위한 기존 클래스의 수정이 지속적으로 발생하게 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Fan {
  ...
  private static int 강풍 = 2;
  ...

  public void push(int signal) {
    ...
    else if (signal == 강풍)
      강풍(); 현재상태=강풍;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;여기서 변경되는 부분은 &lt;code&gt;상태&lt;/code&gt;, 고정되는 부분은 &lt;code&gt;선풍기가 상태에 해당하는 행위를 실행시키는 책임&lt;/code&gt;이다. 아래와 같이 상태를 분리할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interfcae State {
  State action();
}

class Off implements State { State action() { 끈다() } }
class 약풍 implements State { State action() { 약풍() } }

class Fan {
  private int 현재상태;

  public void push(State state) {
    현재상태 = state.action();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;선풍기 객체는 상태 인터페이스만 참조하고 구체적인 구현에 의존하지 않는다. 강풍이 추가된다면 선풍기 객체 Fan에 변경이 필요한 것이 아니라 새로운 상태를 추가하기만 하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class 강풍 implements State { State action() { 강풍() } }&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;싱글톤과의 결합&lt;/h2&gt;
&lt;p&gt;앞선 예제에서 상태를 생성할 때 매번 새로운 인스턴스를 생성할 필요가 없을 경우 싱글톤 패턴을 적용할 수 있다.&lt;/p&gt;
&lt;h1&gt;커맨드 패턴&lt;/h1&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;운동 로봇은 여러 모드가 있는데 그 중 축구를 할 수 있게 하는 축구 모드가 있고 리모컨으로 조정할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SoccerRobot {
  void do() { 축구() }
}

class RemoteController {
  SoccerRobot soccerRobot;

  void push() { soccerRobot.do() }
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;농구 로봇이 필요하면? =&amp;gt; 농구 로봇을 추가하고 리모컨을 수정한다 =&amp;gt; 기존 코드(RemoteController)의 변경이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class BaseballRobot { void do() { 농구() } void push() { soccerRobot.do() } } class RemoteController { BaseballRobot baseballRobot; ... }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;버튼을 눌렀을 때 이전 모드에서 변경된 모드로 동작하게 한다. 이전 모드가 축구라면 농구를, 농구라면 축구를 한다.새로운 로봇이 추가되고 새로운 조건이 추가된다면? =&amp;gt; RemoteController의 수정이 계속 요구된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class RemoteController { final int BASEBALL = 1; final int SOCCER = 2; BaseballRobot baseballRobot; SoccerRobot soccerRobot; int nowState = BASEBALL void push() { if (nowStatue == BASEBALL) nowState = SOCCER; soccerRobot.do(); else if (nowStatus == SOCCER) nowState = BASEBALL; baseballRobot.do(); } }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기서 계속 변하는 것은 버튼을 눌렀을 때 할 행위이다. 이 행위를 캡슐화하여 외부로 추출한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Command { void execut(); }
class SoccerCommand implements Command {
  SoccerRobot soccerRobot;
  void execute() { soccerRobot.do() }
}
class BaseballCommand implements Command {
  BaseballRobot baseballRobot;
  void execute() { baseballRobot.do() }
}

class RemoteController {
  Command command;

  void push() {
    command.execute();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;리모컨의 버튼을 눌렀을 때 일어날 행위를 추가하거나 변경하고 싶다면 &lt;code&gt;Command&lt;/code&gt; 인터페이스를 구현한 구현체만 추가하면 되고 기존 클래스의 변경이 필요하지 않다.&lt;/p&gt;
&lt;h1&gt;옵저버 패턴&lt;/h1&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;경마 게임에서 말이 도착하면 바로 전광판에 보여주는 클래스를 설계한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ArrivalRecorder {
  List&amp;lt;Integer&amp;gt; rank;
  ScoreBoardPrinter scoreBoardPrinter;

  void crossed(Int horseNumber) {
    rank.add(horseNumber);
    ScoreBoardPrinter.update();
  }
}

class ScoreBoardPrinter {
  ArrivalRecorder recorder;

  public void update() { 화면출력(recorder.getRank()); }
}

class Client {
  main() {
    ArrivalRecorder recorder = new ArrivalRecorder();
    ScoreBoardPrinter scoreBoardPrinter = new ScoreBoardPrinter(recorder);
    recoder.scoreBoardPrinter = scoreBoardPrinter;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;다른 종류의 스코어 보드를 사용한다면? 예를들어 소리로 말하는 &lt;code&gt;ScoreBoardSpeaker&lt;/code&gt;로 변경한다면? =&amp;gt; 기존 클래스의 변경이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class ArrivalRecorder { ... ScoreBoardSpeaker scoreBoardSpeaker; ... } class ScoreBoardSpeaker { ... }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;여러 종류의 스코어 보드를 사용한다면? Printer, Speaker 방식 둘 다 사용하고 싶다, 2개의 Speaker를 사용하고 싶다 =&amp;gt; 기존 클래스의 변경이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class ArrivalRecorder { ... List&amp;lt;ScoreBoardSpeaker&amp;gt; scoreBoardSpeaker; ScoreBoardPrinter scoreBoardPrinter; ... }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;변하는 것은 ArrivalRecorder가 알려줘야 할 대상, 즉 Recorder에 새로운 이벤트가 생기면 그 이벤트를 받아서 처리해야되는 대상과 그 대상을 ArrivalRecorder에 등록하는 부분.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Observer { abstract void update(); }

abstract class Subject {
  private List&amp;lt;Observer&amp;gt; observers = new ArrayList&amp;lt;Observer&amp;gt;();

  void attach(Observer observer) { ... }
  void detach(Observer observer) { ... }
  void notify() { 
    for (Observer observer: observers) {
      observer.update()
    }
  }
}

class ArrivalRecorder extends Subject {
  List&amp;lt;Integer&amp;gt; rank;
  void crossed(Int horseNumber) {
    rank.add(horseNumber);
    this.notify();
  }
}

class ScoreBoardPrinter implements Observer {
  void update() { 화면출력(recorder.getRank()); }
}
class ScoreBoardSpeaker implements Observer {
  void update() { 스피커출력(recorder.getRank()); }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;새로운 출력 방식이 필요하면 &lt;code&gt;Observer&lt;/code&gt;를 구현한 새로운 &lt;code&gt;SocreBoard&lt;/code&gt;를 구현하고 &lt;code&gt;ArrivalRecorder&lt;/code&gt;에 등록하면 기존 코드 수정 없이 새로운 출력 방식의 추가나 변경이 가능하도록 된다.&lt;/p&gt;
&lt;h1&gt;데코레이터 패턴&lt;/h1&gt;
&lt;p&gt;여러 기능을 구현한 클래스의 조합을 구현하고자 할 때 사용할 수 있다.&lt;/p&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;스포츠를 할 수 있는 로봇이 있다. 로봇은 현재 축구 기능을 제공한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Robot {
  void do();
}

class SoccerRobot implements Robot {
  void do() {
    축구();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;다른 기능을 하는 로봇이 추가된다면? 예를들어 농구를 하는 로봇, 달리기를 하는 로봇&lt;/li&gt;
&lt;li&gt;여러 기능을 조합한 로봇을 원한다면? 예를들어 농구, 축구를 하는 로봇.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;각 기능별로 &lt;code&gt;Robot&lt;/code&gt; 인터페이스를 구현한 클래스를 만들어야되고, 조합의 경우 모든 구현체에 대한 조합을 하나하나 구현해야 한다. 축구, 농구, 달리기 로봇을 조합하면 축구+농구, 축구+달리기, 농구+달라기 로봇을 구현해야한다.&lt;/p&gt;
&lt;p&gt;각 추가 기능별로 개별 클래스를 설계하고 객체의 조합을 담당하는 클래스(데코레이터)를 만든다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Robot {
  void do();
}

class Defaultrobot implements robot {
  void do() {
    기본_동작();
  }
}


abstract class RobotDecorator extends Robot {
  Robot robot;

  void do() {
    robot.do();
  }
}

class SoccerDecorator extends RobotDecorator {

  SoccerDecorator(Robot robot) {
    super(robot)
  }

  void do() {
    robot.do();
    축구();
  }
}

class BaseballDecorator extends RobotDecorator {

  BaseballDecorator(Robot robot) {
    super(robot)
  }

  void do() {
    robot.do();
    농구();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;다른 기능의 로봇이 필요할 경우 &lt;code&gt;RobotDecorator&lt;/code&gt;를 구현한 신규 기능의 클래스를 구현하고 아래와 같이 클라이언트에서 사용할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class RunningDecorator extends Decorator {
  ...
}

main() {
  Robot runningRobot = new RunningDecorator(new DefaultRobot());
  runningrobot.do();
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;여러 기능을 조합한 로봇의 경우 아래와 같이 구현할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;main() {
  Robot runningAndSoccerRobot = new RunningDecorator(
    new SoccerDecorator(
      new DefaultRobot()));
  runningAndSoccerRobot.draw();
}&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;템플릿 메소드 패턴&lt;/h1&gt;
&lt;p&gt;전체적으로 동일하면서 부분적으로 다른 구문으로 구성된 메소드의 코드 중복을 줄인다. 동일한 기능을 상위 클래스에서 정의하고 변경이 발생하는 부분만 하위 클래스에서 구현한다.&lt;/p&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;차가 출발하려면 문이 닫혀있는지 확인해야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Car {
  move() {
    문_확인()
    출발();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;여기서 여러 종류의 차가 있다면 아래와 같이 설계해볼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Car {
  move();
}

class CarA implements Car {
  move() {
    문_확인();
    CarA방식_출발();
  }
}

class CarB implements Car {
  move() {
    문_확인();
    CarB방식_출발();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;문_확인()&lt;/code&gt; 부분이 계속 중복되는 문제가 있다. 예를들어 &lt;code&gt;창문_확인()&lt;/code&gt; 동작이 추가되어야 한다면? -&amp;gt; &lt;code&gt;Car&lt;/code&gt; 구현체를 모두 찾아 변경해야 한다.&lt;br&gt;여기서 변하는 부분은 출발 전 확인하는 부분. 이 행위를 별도의 클래스로 분리한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;abstrafct class CarTemplate {
  move() {
    문_확인();
    창문_확인();
    moveCar();
  }

  protected abstract moveCar();
}

class CarA extends CarTemplate {
  protected moveCar() {
    CarA방식_출발();
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;다른 방식이 추가된다면 &lt;code&gt;CarTemplate&lt;/code&gt; 클래스의 &lt;code&gt;move()&lt;/code&gt; 메소드만 수정하면 된다.&lt;/p&gt;
&lt;h1&gt;컴퍼짓 패턴&lt;/h1&gt;
&lt;p&gt;객체가 서로 &amp;#39;부분-전체&amp;#39;의 조합을 가지고 전체에서 부분의 공통적인 동작들을 요구할 때 활용할 수 있다.&lt;/p&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;p&gt;A, B 부품으로 구성된 로봇. 각 부품 또는 전체(부품의 합)의 전력 소모 합을 제공한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Robot {
  A a;
  B b;

  int getPower() {
    return a.getPower() + b.getPower() + c.getPower();
  }
}

class A {
  int getPower() {
    return 100;
  }
}

class B {
  int getPower() {
    return 50;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;부품 C가 추가된다면? -&amp;gt; 새로운 클래스 &lt;code&gt;C&lt;/code&gt;를 만들고 &lt;code&gt;Robot&lt;/code&gt;의 멤버를 수정한다. 즉 기존 클래스의 수정이 필요하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class C {
  int getPower() {
    return 400;
  }
}

class Robot {
  ...
  C c;

  int getPower() {
    return a.getPower() + b.getPower() + c.getPower();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;추가되는 클래스를 추상화하고, 추상화된 클래스를 변경없이 다룰 수 있는 구조로 설계한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;interface RobotPart {
  int getPower();
}

class A implements RobotPart { ... }
class B implements RobotPart { ... }
class C implements RobotPart { ... }

class Robot {
  List&amp;lt;RobotPart&amp;gt; robotParts = new ArrayList&amp;lt;&amp;gt;();

  void add(RobotPart part) {
    robotParts.add(part);
  }

  int getPower() {
    return robotParts.getPower().sum()
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9788968480911&amp;amp;orderClick=LAG&amp;amp;Kc=&quot;&gt;Java 객체지향 디자인 패턴 / 정인상, 채홍성 지음 / 한빛미디어&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Program language/Java</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/63</guid>
      <comments>https://rura6502.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-cheat-sheet#entry63comment</comments>
      <pubDate>Sun, 10 Apr 2022 07:01:18 +0900</pubDate>
    </item>
    <item>
      <title>run java project with h2  without installation</title>
      <link>https://rura6502.tistory.com/entry/run-java-project-with-h2-without-installation</link>
      <description>&lt;pre&gt;&lt;code class=&quot;language-groovy&quot;&gt;implementation &amp;#39;com.h2database:h2:1.4.200&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class App {
  public static void main(String[] args) throws Exception {

    final String[] H2_ARGS = new String[]{
      &amp;quot;-web&amp;quot;
      , &amp;quot;-webPort&amp;quot;, &amp;quot;9091&amp;quot;
      , &amp;quot;-webAdminPassword&amp;quot;, &amp;quot;&amp;quot;
      , &amp;quot;-browser&amp;quot;
      , &amp;quot;-tcp&amp;quot;
      , &amp;quot;-tcpAllowOthers&amp;quot;
      , &amp;quot;-tcpPort&amp;quot;, &amp;quot;9090&amp;quot;
      , &amp;quot;-key&amp;quot;, &amp;quot;test&amp;quot;, &amp;quot;test&amp;quot;
    };


    Server h2TcpServer = Server.createTcpServer(H2_ARGS);
    Server h2WebServer = Server.createWebServer(H2_ARGS);
    h2TcpServer.start();
    h2WebServer.start();
    System.out.println(h2TcpServer.getStatus());
    System.out.println(h2WebServer.getStatus());
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Create EntityManager&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-groovy&quot;&gt;implementation &amp;#39;org.hibernate:hibernate-entitymanager:5.6.4.Final&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Class.forName(&amp;quot;org.h2.Driver&amp;quot;);
Connection con = DriverManager.getConnection(
&amp;quot;jdbc:h2:mem:tcp://localhost:9090/test&amp;quot;, &amp;quot;sa&amp;quot;, &amp;quot;&amp;quot;);

EntityManagerFactory emf =
Persistence.createEntityManagerFactory(&amp;quot;test_persistence_unit&amp;quot;);
EntityManager entityManager = emf.createEntityManager();&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;// /main/resources/META-INF/persistence.xml
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;persistence xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;
             version=&amp;quot;2.2&amp;quot; xmlns=&amp;quot;http://xmlns.jcp.org/xml/ns/persistence&amp;quot;
             xsi:schemaLocation=&amp;quot;http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd&amp;quot;&amp;gt;
  &amp;lt;persistence-unit name=&amp;quot;test_persistence_unit&amp;quot;&amp;gt;
    &amp;lt;properties&amp;gt;
      &amp;lt;property name=&amp;quot;javax.persistence.jdbc.driver&amp;quot; value=&amp;quot;org.h2.Driver&amp;quot;/&amp;gt;
      &amp;lt;property name=&amp;quot;javax.persistence.jdbc.user&amp;quot; value=&amp;quot;sa&amp;quot;/&amp;gt;
      &amp;lt;property name=&amp;quot;javax.persistence.jdbc.password&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;
      &amp;lt;property name=&amp;quot;javax.persistence.jdbc.url&amp;quot; value=&amp;quot;jdbc:h2:mem:tcp://localhost:9090/test&amp;quot;/&amp;gt;
      &amp;lt;property name=&amp;quot;hibernate.dialect&amp;quot; value=&amp;quot;org.hibernate.dialect.H2Dialect&amp;quot;/&amp;gt;

      &amp;lt;property name=&amp;quot;hibernate.show_sql&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
      &amp;lt;property name=&amp;quot;hibernate.format_sql&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
      &amp;lt;property name=&amp;quot;hibernate.use_sql_comments&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
    &amp;lt;/properties&amp;gt;
  &amp;lt;/persistence-unit&amp;gt;
&amp;lt;/persistence&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Program language/Java</category>
      <category>EntityManager</category>
      <category>H2</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/61</guid>
      <comments>https://rura6502.tistory.com/entry/run-java-project-with-h2-without-installation#entry61comment</comments>
      <pubDate>Sat, 22 Jan 2022 10:46:10 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security - Authentication Architecture: Example</title>
      <link>https://rura6502.tistory.com/entry/Spring-Security-Authentication-Architecture-Example</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;a href=&quot;https://rura6502.tistory.com/entry/Spring-Security-Authentication-Architecture&quot;&gt;이전 글&lt;/a&gt;에서 스프링 시큐리티가 &lt;code&gt;인증&lt;/code&gt;을 위해 어떤 설계로 구성, 동작하는지 전반적인 아키텍처를 살펴보았다. 객체간의 호출 관계를 텍스트로만 보면 약간 이해가 어려울 수 있는데 공식 도큐먼트에서 설명하고있는 예제를 통해 더 쉽게 이해할 수 있다.&lt;/p&gt;
&lt;h1&gt;Form Login&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 폼 로그인의 아이디, 비밀번호를 폼에 입력해서 인증받을 수 있는 가장 일반적인 경우이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;임의의 url로 접근했을 때 로그인 페이지로 리다이렉션되기까지...&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sa5xP/btrosxu6Dat/4t1YwuSi77FmfA8kfifsv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sa5xP/btrosxu6Dat/4t1YwuSi77FmfA8kfifsv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sa5xP/btrosxu6Dat/4t1YwuSi77FmfA8kfifsv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsa5xP%2Fbtrosxu6Dat%2F4t1YwuSi77FmfA8kfifsv0%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;1115&quot; height=&quot;495&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 클라이언트의 &lt;code&gt;/private&lt;/code&gt; 요청&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 클라이언트가 &lt;code&gt;/private&lt;/code&gt;라는 api를 요청했다. 여기서 중요한점은 클라이언트는 &lt;code&gt;인증&lt;/code&gt;과정에서 &lt;code&gt;익명&lt;/code&gt;으로 판단된 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 접근 거부, AccessDeniedException&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 익명 클라이언트의 &lt;code&gt;/private&lt;/code&gt; 요청은 &lt;code&gt;SecurityFilterChain&lt;/code&gt;에서 익명 클라이언트에게 제공할 수 없다는 판단을 하게되고 이는 &lt;code&gt;AccessDeniedException&lt;/code&gt;을 발생시킨다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. &lt;code&gt;/login&lt;/code&gt; 페이지로 리다이렉션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;code&gt;AccessDeniedException&lt;/code&gt;은 &lt;a href=&quot;https://rura6502.tistory.com/entry/Spring-Security-Architectrue?category=723090&quot;&gt;Spring Security - Architecture&lt;/a&gt; 에서 마지막에 언급했던, 스프링 시큐리티에서 두가지 메인 익셉션인 &lt;code&gt;AuthenticationException&lt;/code&gt;, &lt;code&gt;AccessDeniedException&lt;/code&gt;를 처리하는 필터인 &lt;code&gt;ExceptionTranslationFilter&lt;/code&gt;로 전달되고, &lt;code&gt;ExceptionTranslationFilter&lt;/code&gt;는 &lt;code&gt;AuthenticationEntriyPoint&lt;/code&gt;에 설정된 페이지로 리다이렉션시키는 응답을 클라이언트에 전송된다. 기본 설정의 경우 &lt;code&gt;LoginUrlAuthenticationEntryPoint&lt;/code&gt;가 인스턴스로 사용된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로그인 요청&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IEG1O/btrox1nVE60/dkdGr5QHDQt76Vom4IqQR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IEG1O/btrox1nVE60/dkdGr5QHDQt76Vom4IqQR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IEG1O/btrox1nVE60/dkdGr5QHDQt76Vom4IqQR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIEG1O%2Fbtrox1nVE60%2FdkdGr5QHDQt76Vom4IqQR1%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;771&quot; height=&quot;702&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 유저 정보를 인증하기 위한 필터&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 클라이언트가 폼을 통해 username, password를 입력했다면 해당 요청은 스프링 시큐리티가 제공하는 기본 필터들 중 &lt;code&gt;UsernamePasswordAuthenticationFilter&lt;/code&gt;가 인증을 담당하게 된다. &lt;code&gt;UsernamePasswordAuthenticationFilter&lt;/code&gt;는 받은 &lt;code&gt;HttpServletRequest&lt;/code&gt; 정보(username, password)를 조합해서 &lt;code&gt;Authentication&lt;/code&gt;의 구현체인 &lt;code&gt;UsernamePasswordAuthenticationToken&lt;/code&gt;을 생성한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. AuthenticatinManager&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 생성된 &lt;code&gt;UsernamePasswordAuthenticationToken&lt;/code&gt;는 실제 인증 과정을 거치기 위해 &lt;code&gt;AuthenticationManager&lt;/code&gt;로 전달된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 정보 인증에 실패했을 때&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 인증 정보가 실패하면 앞서 저장했던 &lt;code&gt;SecurityContextHolder&lt;/code&gt;의 내용이 비워지게 되고 &lt;code&gt;AuthenticationFailureHandler&lt;/code&gt;에 의해 다음 액션이 정의된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 정보 인증에 성공했을 때&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 인증 정보가 &lt;code&gt;SecurityContextHolder&lt;/code&gt;에 저장되고 다양한 모듈들이 호출되는데, 세션 관련 작업을 위한 &lt;code&gt;SessionAuthenticationStrategy&lt;/code&gt;, 로그인 성공 이벤트를 위한 &lt;code&gt;AuthenticationSucessHandler&lt;/code&gt;가 호출되고 되고 &lt;code&gt;AppliationEventPublisher&lt;/code&gt;에서 &lt;code&gt;InteractiveAuthenticationSuccessEvent&lt;/code&gt; 발생시킨다.&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&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://docs.spring.io/spring-security/reference/index.html&quot;&gt;Spring Security Document&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Framework &amp;amp; Library &amp;amp; Tool/Spring&amp;amp;SpringBoot</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/51</guid>
      <comments>https://rura6502.tistory.com/entry/Spring-Security-Authentication-Architecture-Example#entry51comment</comments>
      <pubDate>Tue, 21 Dec 2021 23:17:11 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security - Authentication Architecture</title>
      <link>https://rura6502.tistory.com/entry/Spring-Security-Authentication-Architecture</link>
      <description>&lt;p&gt;  Spring Security - Architectrue에서 설명했던 아키텍쳐는 서블릿에서 스프링 시큐리티가 어떻게 Servlet 구조에서 인증, 인가를 하기위해 어떻게 동작하는지 살펴보았다. 클라이언트 요청이 일련의 필터체인을 거치면서 각 체인에 필요한 단계들을 거치는데 이러한 단계 뿐만 아니라 실제 애플리케이션 로직에서 사용할 공통적인 정보들을 공유하기 위해 &lt;code&gt;ThreadLocal&lt;/code&gt;에 일련의 정보들을 보관하고 관리하는데 &lt;code&gt;SecurityContextHolder&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhw523/btrosxPiDYd/Z2ZNIHOYigfMwUObEOWxl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhw523/btrosxPiDYd/Z2ZNIHOYigfMwUObEOWxl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhw523/btrosxPiDYd/Z2ZNIHOYigfMwUObEOWxl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdhw523%2FbtrosxPiDYd%2FZ2ZNIHOYigfMwUObEOWxl0%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;486&quot; height=&quot;179&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;  위 그림의 &lt;code&gt;SecurityContextHolder&lt;/code&gt;를 보면 여러 내부 하위 정보를 감싸는 형태로 구성되어 있는데, 실제 인증, 권한 정보를 저장하는 &lt;code&gt;Principal&lt;/code&gt;, &lt;code&gt;Credentials&lt;/code&gt;, &lt;code&gt;Authorities&lt;/code&gt; 객체를 &lt;code&gt;Authentication&lt;/code&gt;이라는 하나의 객체가 감싸고 있고 이를 &lt;code&gt;SecurityContext&lt;/code&gt;가 감싸고 있다. &lt;code&gt;SecurityContext&lt;/code&gt;가 스프링 시큐리티에서 다루는 실질적인 정보를 담고있는 모음?이고 &lt;code&gt;SecurityContextHodler&lt;/code&gt;는 이 정보를 관리하는 역할이다. 예를들어 하나의 쓰레드에서만 공유가능한 형태로 관리할 것인지, 애플리케이션 전체에서 하나의 &lt;code&gt;SecurityContext&lt;/code&gt;만 사용할 것인지 따위이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  예를들어, 웹 애플리케이션 같은 서버-클라이언트 애플리케이션이 아니라 swing등을 사용한 일반 프로그램의 경우 개별 쓰레드가 별도의 인증정보를 담고있는 형태는 아니기 때문에 &lt;code&gt;SecurityContextHodler&lt;/code&gt;에서 &lt;code&gt;SecurityContext&lt;/code&gt;의 범위를 조정해줄 수 있는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;  쓰레드 로컬에 저장된 정보는 하나의 요청 당 하나의 쓰레드를 배정하는 컨테이너 특성에 따라 하나의 쓰레드가 통과하는 모든 애플리케이션 로직에서 해당 정보를 참조할 수 있게 되는데 예를들어 스프링 시큐리티를 통해 인증된 사용자가 서비스를 요청할 경우에 모든 서비스 코드에서 쓰레드 로컬에 저장된 사용자의 정보를 아래와 같이 받아올 수 있으며 생성 또한 가능하다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  쓰레드 로컬에 저장된 정보는 다른 쓰레드에서 접근이 불가능하므로 안전하고, 만약 해당 쓰레드를 재사용할 경우 정보 노출을 막기 위해 &lt;code&gt;FilterChainProxy&lt;/code&gt;가 정보를 모두 삭제한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;// 정보 받아오기
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new TestingAuthenticationToken(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, &amp;quot;ROLE_USER&amp;quot;);
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);


// 정보 생성
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection&amp;lt;? extends GrantedAuthority&amp;gt; authorities = authentication.getAuthorities();&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;AuthenticationManager, ProviderManager&lt;/h2&gt;
&lt;p&gt;  앞에서 나왔던 &lt;code&gt;SecurityContext&lt;/code&gt;는 &lt;code&gt;Authentication&lt;/code&gt; 객체를 포함하고 있고 &lt;code&gt;Authentication&lt;/code&gt; 객체는 &lt;code&gt;Principal&lt;/code&gt;, &lt;code&gt;Credentials&lt;/code&gt;, &lt;code&gt;Authorizes&lt;/code&gt; 객체를 포함하고 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;principal : 실제 유저 정보(아이디, 비밀번호)를 가지고 있음&lt;/li&gt;
&lt;li&gt;credentials : 비밀번호 정보 등 보안정보를 가지고 있음&lt;/li&gt;
&lt;li&gt;authorities : 인가에 필요한 권한 정보를 담고 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;  &lt;code&gt;authentication&lt;/code&gt;은 인터페이스이며 각 인증 방식에 따라 다앙한 구현체가 있다. 예를들어 아이디/비밀번호 인증방식을 사용할 경우 &lt;code&gt;Authentication&lt;/code&gt; 인터페이스는 &lt;code&gt;UsernamePasswordAuthenticationToken&lt;/code&gt; 구현체로 구현된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;  credentials에 담긴 정보는 필터를 거치면서 지워진다. 실제로 필터에서 해당 정보를 보면 비밀번호 정보가 있는데 서비스 레이어에서 보면 null로 되어 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;  이렇게 합쳐진 3개의 정보를 가진 &lt;code&gt;Authentication&lt;/code&gt;은 &lt;code&gt;AuthenticationManager&lt;/code&gt;의 input이기도 한데, &lt;code&gt;AuthenticationManager&lt;/code&gt;가 전달받은 &lt;code&gt;Authentication&lt;/code&gt;이 유효한지, 안한지에 대해 판단하는 역할을 수행하게 된다. 보통 스프링에 구현되어있는, 일반적으로 사용되는 &lt;code&gt;AuthenticationManager&lt;/code&gt;의 구현체는 &lt;code&gt;ProviderManager&lt;/code&gt;인데 &lt;code&gt;ProviderManager&lt;/code&gt;는 하나 이상의 &lt;code&gt;AuthenticationProvider&lt;/code&gt;를 가지고 들어온 요청이 유효한지 안한지 확인하는데 우리가 일반적으로 말하는 인증 방식을 해석하는 곳이 &lt;code&gt;AuthenticationProviders&lt;/code&gt;에 해당한다고 보면된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yE06h/btrox0CtR6J/utkCnyxq8cMqOuI3UOspW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yE06h/btrox0CtR6J/utkCnyxq8cMqOuI3UOspW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yE06h/btrox0CtR6J/utkCnyxq8cMqOuI3UOspW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyE06h%2Fbtrox0CtR6J%2FutkCnyxq8cMqOuI3UOspW0%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;637&quot; height=&quot;305&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  예를들어 id, password 인증 방식의 경우 id, password 인증 방식을 구현한 &lt;code&gt;AuthenticationProvider&lt;/code&gt;가 &lt;code&gt;ProviderManager&lt;/code&gt;에 제공되어서, 요청이 들어오면 해당 요청에 대한 &lt;code&gt;Authentication&lt;/code&gt; 객체가 만들어지고, &lt;code&gt;AuthenticationManager&lt;/code&gt;이 현재 보유중인 &lt;code&gt;AuthenticationProvider&lt;/code&gt;들을 체크해가며 인증이 유효한지 안한지 체크해서 그 결과를 반환하게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;  &lt;code&gt;AuthenticationProvider&lt;/code&gt;가 하나 이상인 이유는 애플리케이션이 여러개의 인증 방식을 제공할 수 있는 경우이다. 예를들어 웹 애플리케이션의 경우 form을 통한 id/password 인증 방식도 제공하면서, 토큰 방식도 제공하고 싶다고 한다면 id/password를 위한 프로파이더, 토큰을 위한 프로바이더를 등록하면 되는것이다. &lt;code&gt;ProviderManager&lt;/code&gt;는 이러한 동작을 수행하기 위해, 인증이 실패할 경우 바로 익셉션을 발생 시키는 것이 아니라 현재 사용가능한 모든 프로바이더들을 interate하면서 인증을 검증한다. &lt;code&gt;ProviderManager&lt;/code&gt;는 하나의 &lt;code&gt;AuthenticationProvider&lt;/code&gt;가 실패했다고 끝내는 것이 아니라, 그 다음 &lt;code&gt;AuthenticationProvider&lt;/code&gt;를 체크하도록 설계되어 있으며 인증 작업이 끝나면 &lt;code&gt;Authentication&lt;/code&gt;을 반환하는데, 이 반환 값의 credential(비밀번호 등) 정보를 지우는 역할도 수행한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  credential 정보를 지우는 동작 여부는 &lt;code&gt;ProvidoerManager&lt;/code&gt;가 제공하는 &lt;code&gt;eraseCredentialsAfterAuthentication&lt;/code&gt;의 값을 조정함으로써 제어할수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;AbstractAuthenticationProcessingFilter&lt;/h2&gt;
&lt;p&gt;  인증을 수행하기 위해 스프링 시큐리티에서 제공하는 &lt;code&gt;AuthenticationManager&lt;/code&gt;, &lt;code&gt;ProviderManager&lt;/code&gt;를 살펴보았는데 이제 이것들이 스프링 시큐리티의 필터 체인과 어떻게 동작할까라는 물음의 답은 &lt;code&gt;필터&lt;/code&gt;이다. 이러한 일련의 과정들은 &lt;code&gt;AbstractAuthenticationProcessingFilter&lt;/code&gt; 인터페이스를 구현한 필터 구현체에서 사용된다. &lt;a href=&quot;https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html&quot;&gt;&lt;code&gt;AbstractAuthenticationProcessingFilter&lt;/code&gt;&lt;/a&gt;를 보면 &lt;code&gt;AuthenticationManager&lt;/code&gt;와 연관된 메소드들을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMjX27/btroxkBuU6a/8mUZBvJ7KUUiUlaVAan5y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMjX27/btroxkBuU6a/8mUZBvJ7KUUiUlaVAan5y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMjX27/btroxkBuU6a/8mUZBvJ7KUUiUlaVAan5y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMjX27%2FbtroxkBuU6a%2F8mUZBvJ7KUUiUlaVAan5y1%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;786&quot; height=&quot;714&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/index.html&quot;&gt;Spring Security Docuemnt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Framework &amp;amp; Library &amp;amp; Tool/Spring&amp;amp;SpringBoot</category>
      <category>AuthenticationManager</category>
      <category>ProviderManager</category>
      <category>Spring Security</category>
      <category>스프링 시큐리티</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/49</guid>
      <comments>https://rura6502.tistory.com/entry/Spring-Security-Authentication-Architecture#entry49comment</comments>
      <pubDate>Tue, 21 Dec 2021 21:35:49 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security - Architecture</title>
      <link>https://rura6502.tistory.com/entry/Spring-Security-Architectrue</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 보안(Security)에는 크게 두가지 개념, &lt;code&gt;인증(authentication)&lt;/code&gt;과 &lt;code&gt;인가(authorization)&lt;/code&gt; 부분으로 나뉜다. &lt;code style=&quot;letter-spacing: 0px; text-align: center;&quot;&gt;인증&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px; text-align: center;&quot;&gt;은 이 사용자가 현재 사용자가 누구인지 확인하는 과정이며 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px; text-align: center;&quot;&gt;인가&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px; text-align: center;&quot;&gt;는 이 사용자가 특정 리소스를 사용할 자격이 있는지에 대한 부분이다. 예를들어 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px; text-align: center;&quot;&gt;로그인&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px; text-align: center;&quot;&gt;을 한다는 행위는 사용자가 누구인지 특정할 수 있는 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px; text-align: center;&quot;&gt;인증&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px; text-align: center;&quot;&gt;에 해당하고, 로그인 한 사용자가 일반 사용자인지, 관리자인지에 따라 관리자 페이지를 보여줄지 등을 결정하는 과정이 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px; text-align: center;&quot;&gt;인가&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px; text-align: center;&quot;&gt; 과정이다.&lt;/span&gt; 스프링 시큐리티는 애플리케이션에 대한 인증, 인가를 지원하기 위해 여러개의 필터들로 구성된 필터 체인과 연관된 여러 모듈로 구성되어 있다. 이러한 필터들을 개발자가 직접 생성 또는 수정할 수 있도록 되어있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;오염된 물을 정수하기 위해서 여러개의 필터를 장착하고 각 필터가 해당 필터의 단계, 기능에 맞는 오염 물질을 거르고 다음 필터로 넘겨주는, 정수기와 같은 구조로 이루어졌다고 생각해보면 이해하기 쉽다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Servlet Filter to Spring Security Filter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 잠깐 설명했던 스프링 시큐리이티의 필터는 서블렛의 &lt;code&gt;Filter&lt;/code&gt; 구현체인데 스프링 시큐리티의 필터 중 하나인 &lt;code&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/logout/LogoutFilter.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LogoutFilter&lt;/a&gt;&lt;/code&gt;를 보면 &lt;code&gt;javax.servlet.Filter&lt;/code&gt;를 구현했음을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xbdua/btroplAL5VT/kImfeDAUFhBT4qyY76exC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xbdua/btroplAL5VT/kImfeDAUFhBT4qyY76exC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xbdua/btroplAL5VT/kImfeDAUFhBT4qyY76exC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXbdua%2FbtroplAL5VT%2FkImfeDAUFhBT4qyY76exC0%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;295&quot; height=&quot;140&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 서블릿의 필터들은 아래 그림처럼 클라이언트의 요청이 들어오고 요청에 대한 응답을 했을 때, 일종의 단계로써 중간에 끼워넣을 수 있고 이러한 필터들을 &lt;code&gt;FilterChain&lt;/code&gt;이라는 이름으로 하나로 묶을 수 있다. 여기서 필터들이 하는 일은 서블릿 컨테이너가 서블릿에 전달할 &lt;code&gt;HttpServletRequest&lt;/code&gt;, &lt;code&gt;HttpServletResponse&lt;/code&gt;를 컨트롤(읽기, 쓰기)할 수 있다. 필터는 전달하는 방향에에 따라 정방향 호출 순서로만 호출되기 때문에 순서에 유의하여야 하는 특성이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj3kJg/btrorOJaJAq/n7Wb9MLNRUpQQ8P2Kgek6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj3kJg/btrorOJaJAq/n7Wb9MLNRUpQQ8P2Kgek6K/img.png&quot; data-alt=&quot;FilterChain&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj3kJg/btrorOJaJAq/n7Wb9MLNRUpQQ8P2Kgek6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj3kJg%2FbtrorOJaJAq%2Fn7Wb9MLNRUpQQ8P2Kgek6K%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;157&quot; height=&quot;227&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FilterChain&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 스프링 시큐리티는 보안에 필요한 시큐리티용 필터들을 기존 애플리케이션에 동작중인 필터에 바로 끼워 넣는 것이 아니라 &lt;code&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/5.3.13/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DelegatingFilterProxy&lt;/a&gt;&lt;/code&gt;라는 필터를 하나 끼워넣고, 스프링의 라이프사이클과 빈 등록 메커니즘을 적용하기 위해 스프링의 &lt;code&gt;ApplicationContext&lt;/code&gt;와 연결할 수 있도록 연결하였다. &lt;code&gt;DelegatingFilterProxy&lt;/code&gt;의 역할은 스프링에서 빈으로 등록한 필터 정보들을 읽어서 서블릿 필터 체인 사이에서 동작할 수 있도록 세팅하는 역할을 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m6JbD/btror1vN4ek/eApQezrur5EfbTVH12Lan0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m6JbD/btror1vN4ek/eApQezrur5EfbTVH12Lan0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m6JbD/btror1vN4ek/eApQezrur5EfbTVH12Lan0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm6JbD%2Fbtror1vN4ek%2FeApQezrur5EfbTVH12Lan0%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;456&quot; height=&quot;338&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 이 &lt;code&gt;DelegatingFilterProxy&lt;/code&gt;는 &lt;code&gt;FilterChainProxy&lt;/code&gt;라는 빈래핑하고 있는데 &lt;code&gt;FilterChainProxy&lt;/code&gt;는 실제 스프링 시큐리티의 &lt;code&gt;SecurityFilterChain&lt;/code&gt;을 호출하는 호출자로써 동작한다. &lt;code&gt;FilterChainProxy&lt;/code&gt;의 역할은 빈으로 등록되어 있는 하나 이상의 &lt;code&gt;SecurityFilterChain&lt;/code&gt;을 &lt;code&gt;HttpServletRequest&lt;/code&gt;의 값에 따라 어떤 &lt;code&gt;SecurityFilterChain&lt;/code&gt;을 사용할 지 결정하는 역할을 할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp; 정수기에서 사용자가 임의로 자유롭게 필터를 넣었다 뺄 수 있는 공간(DelegatingFilterProxy)이 있으며 그 공간은 실제로 여러개의 방(SecurityFilterChain)으로 나누어져있고 그 방에 사용자의 입맛에 따라 여러개의 필터(SecurityFilter)를 장착할 수 있다. 각 방은 서로에게 독립적이며 물은 하나의 방에서만 흐를 수 있으며 물이 공급되는 곳에선 물 색깔, 농도, 상태에 따라 어느 필터방으로 물을 보낼지 사용자가 설정할 수 있는 수도꼭지(FilterChainProxy)가 있다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biwUBx/btroqSMTzpZ/p1xWxRgtdbK4TJhBxMXuD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biwUBx/btroqSMTzpZ/p1xWxRgtdbK4TJhBxMXuD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biwUBx/btroqSMTzpZ/p1xWxRgtdbK4TJhBxMXuD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiwUBx%2FbtroqSMTzpZ%2Fp1xWxRgtdbK4TJhBxMXuD0%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;532&quot; height=&quot;365&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Security Exception, ExcetpionTranslationFilter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 스프링 시큐리티 필터는 수많은 필터들이 기본적으로 제공되는데 그 중 익셉션 처리를 위한 특별한 필터인 &lt;code&gt;ExceptionTranslationFilter&lt;/code&gt;라는 필터는 스프링 시큐리티에서 발생하는 대표 익셉션인 &lt;code&gt;AccessDeniedException&lt;/code&gt;, &lt;code&gt;AuthenticationException&lt;/code&gt;을 처리하는 필터이다. 이 필터는 요청이 들어올 경우 설정에 따라 인증된 사용자일 경우 요청을 다음 필터로 진행시키고 인증이 안되었을 경우 &lt;code&gt;AuthenticationEntryPoint&lt;/code&gt;을 통해 클라이언트(사용자)에게 인증을 요청하는 행위를 수행한다. 스프링 시큐리티에서 시큐리티를 활성화시키고 별도의 설정을 하지 않았을 경우 모든 api의 요청이 login페이지로 리다이렉션되는 것을 볼 수 있는데 이 역할을 &lt;code&gt;AuthenticationEntryPoint&lt;/code&gt;가 하는 것이다. 접근이 완전 거부될 경우 발생하는 &lt;code&gt;AccessDeniedException&lt;/code&gt;의 경우 이 필터에서 &lt;code&gt;AccessDeniedHandler&lt;/code&gt;로 보내지게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px;&quot;&gt;&amp;nbsp; 즉 '인증되지 않은 사용자는 어떻게 처리할까?' &lt;/span&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px;&quot;&gt;라는 행동을 정의하기 위해선 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;AuthenticationEntryPoint&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px;&quot;&gt;를 직접 구현해서 설정해야되고 접근 거부가 되었을 경우 행위에 대해선 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;AccessDeniedHandler&lt;/code&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px;&quot;&gt;를 구현해야 한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;h1&gt;Reference&lt;/h1&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://docs.spring.io/spring-security/reference/&quot;&gt;Spring Security Document&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Framework &amp;amp; Library &amp;amp; Tool/Spring&amp;amp;SpringBoot</category>
      <category>Spring Security</category>
      <category>스프링 시큐리티</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/48</guid>
      <comments>https://rura6502.tistory.com/entry/Spring-Security-Architectrue#entry48comment</comments>
      <pubDate>Tue, 21 Dec 2021 16:21:14 +0900</pubDate>
    </item>
    <item>
      <title>log4j에서 사용하는 로그 메세지 포맷</title>
      <link>https://rura6502.tistory.com/entry/log4j%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%A1%9C%EA%B7%B8-%EB%A9%94%EC%84%B8%EC%A7%80-%ED%8F%AC%EB%A7%B7</link>
      <description>&lt;p&gt;slf4j에서 로그를 쓸 때 아래와 같은 포맷이 매우 편해서 어떤 모듈을 사용하는지 궁금해서 라이브러리를 오픈해보았다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;log.error(&amp;quot;find by id = {}&amp;quot;, id);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;지금 프로젝트는 log4j 구현체를 사용하고 있었는데 &lt;code&gt;org.apache.logging.log4j.message.ParameterizedMessage&lt;/code&gt;라는 구현체를 사용하고 있었다.&lt;/p&gt;
&lt;p&gt;Exception 메세지 &lt;code&gt;+&lt;/code&gt;로 파라미터들을 메세지와 조합하는게 번거롭고 &lt;code&gt;String.format&lt;/code&gt;을 사용하기엔 타입도 고려해줘야되고 귀찮은 점이 많았는데 이 구현체를 사용해서 아래 처럼 활용할 수 있을 것 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import org.apache.logging.log4j.message.ParameterizedMessage;

public Class CutomException extends RunetimeException {

  public CustomException(final String message, Object... args) {
      super(Parameterized Message.format(message, args));
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Framework &amp;amp; Library &amp;amp; Tool/etc</category>
      <category>log message format</category>
      <category>LOG4J</category>
      <category>ParameterizedMessage</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/47</guid>
      <comments>https://rura6502.tistory.com/entry/log4j%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%A1%9C%EA%B7%B8-%EB%A9%94%EC%84%B8%EC%A7%80-%ED%8F%AC%EB%A7%B7#entry47comment</comments>
      <pubDate>Sat, 18 Dec 2021 15:30:17 +0900</pubDate>
    </item>
    <item>
      <title>Servlet, Servlet Container, Spring Container</title>
      <link>https://rura6502.tistory.com/entry/Servlet-Servlet-Container-Spring-Container</link>
      <description>&lt;h1&gt;Servlet&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Servlet(서블릿)&lt;/code&gt;이란 자바 클래스 종류 중 하나로 요청-응답 프로그래밍 모델을 제공하는 서버(일반적인 경우 web)의 기능을 구현, 확장하기 위한 클래스이다. &lt;code&gt;javax.servlet&lt;/code&gt;, &lt;code&gt;javax.servlet.http&lt;/code&gt; 패키지에 서블릿 구현을 위한 인터페이스와 클래스들이 있다. 모든 서블릿 구현체들은 서블릿 라이프사이클을 정의한 &lt;code&gt;Servlet&lt;/code&gt; 인터페이스를 구현해야 하며 일반적인 서비스를 구현할 경우 &lt;code&gt;GenericServlet&lt;/code&gt;를 사용/상속해서 구현할 수 있다. &lt;code&gt;HttpServlet&lt;/code&gt;의 경우 HTTP 서비스를 핸들링하기 위한 &lt;code&gt;doGet&lt;/code&gt;, &lt;code&gt;doPost&lt;/code&gt; 메소드를 제공하고 있다.&lt;/p&gt;
&lt;h2&gt;Servlet.java&lt;/h2&gt;
&lt;p&gt;총 5개 &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/servlet/Servlet.html&quot;&gt;실제 서블릿 코드&lt;/a&gt;를 보면 총 5개의 미구현 메소드가 있다. 이 메소드들은 서블릿의 라이프 사이클에 따라 호출되는 메소드 들이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;405&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cE5Vj5/btrnqxCIjyo/XdcvBUCUHTsnqpBcZTVv1K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cE5Vj5/btrnqxCIjyo/XdcvBUCUHTsnqpBcZTVv1K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cE5Vj5/btrnqxCIjyo/XdcvBUCUHTsnqpBcZTVv1K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcE5Vj5%2FbtrnqxCIjyo%2FXdcvBUCUHTsnqpBcZTVv1K%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;272&quot; height=&quot;294&quot; data-origin-width=&quot;405&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Serlvet class is loaded&lt;/code&gt; : 요청이 발생했을 때 서블릿 클래스가 서블릿 컨테이너에 의해 로딩된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Servlet instance is created\&lt;/code&gt; : 서블릿 컨테이너는 서블릿 클래스를 읽어서 인스턴스를 생성하는데 이 인스턴스는 서블릿 라이프사이클에서 고유하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;init method is invoked&lt;/code&gt; : 서블릿 컨테이너가 서블릿의 &lt;code&gt;init()&lt;/code&gt; 메소드를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service method is invoked&lt;/code&gt; : 요청을 처리하기 위한 &lt;code&gt;service()&lt;/code&gt; 메소드를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;destroy method is invoked&lt;/code&gt; : 서블릿 인스턴스가 삭제되기 전에 &lt;code&gt;deploy()&lt;/code&gt; 메소드를 호출한다. 이 메소드를 통해서 스트림을 닫거나 스레드를 정리할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Servlet Container, Servlet Context&lt;/h1&gt;
&lt;p&gt;앞서 설명했다시피 서블릿은 요청-응답 모델에서 서버가 응답하기 위해 하나의 서블릿이 어떻게 동작해야할지에 대한 스펙이며 개발자는 애플리케이션 서비스를 위해 서블릿 구현체를 구현해야 한다. 이 서블릿 구현체를 구동시켜주는 것이 &lt;code&gt;Servlet Container(서블릿 컨테이너)&lt;/code&gt;이다. 서블릿 컨테이너는 일반적으로 HTTP를 처리할 수 있는 웹 서버 형태로 HTTP 요청이 들어오면 해당 요청을 처리할 수 있는 적절한 서블릿을 찾아 앞서 설명했던 라이프사이클에 따라 서비스를 제공하는 역할을 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rDbSM/btrnqyO9Vzw/voGSPZHAf2PydbCH3VjJXk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rDbSM/btrnqyO9Vzw/voGSPZHAf2PydbCH3VjJXk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rDbSM/btrnqyO9Vzw/voGSPZHAf2PydbCH3VjJXk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrDbSM%2FbtrnqyO9Vzw%2FvoGSPZHAf2PydbCH3VjJXk%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;409&quot; height=&quot;233&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;가장 대중적으로 잘 알려진 웹 서블릿 컨테이너, 웹 컨테이너로는 tomcat이 있다. 톰캣은 web server를 가지고 있으면서 HTTP 요청이 들어오면 서블릿 컨테이너로 요청을 넘기고 서블릿 컨테이너는 적절한 서블릿을 읽어서 구동, 즉 라이프사이클을 한번 돌리는 작업을 한다. 이 때 요청을 처리하기 위한 플로우를 위해 요청 당 하나의 스레드를 할당한다. 요청에 대한 작업이 끝나면 서블릿 컨테이너는 서블릿이 반환하는 결과를 적절한 모양으로 적절한 위치에 반환하는 역할을 하게 되는데 톰캣같은 웹 컨테이너의 경우 http 요청에 대한 결과 값 반환이 그 역할이다.&lt;/p&gt;
&lt;h1&gt;Servlet Context&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6p7gb/btrnwytfG3p/GDTBS5rZvAok3DSQmG9wxk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6p7gb/btrnwytfG3p/GDTBS5rZvAok3DSQmG9wxk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6p7gb/btrnwytfG3p/GDTBS5rZvAok3DSQmG9wxk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6p7gb%2FbtrnwytfG3p%2FGDTBS5rZvAok3DSQmG9wxk%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;472&quot; height=&quot;171&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;서블릿 컨테이너는 서블릿들이 공통적으로 참조할 수 있는 오브젝트를 하나 생성하는데, 이것이 바로 &lt;code&gt;Servlet Context(서블릿 컨텍스트)&lt;/code&gt;이다. 개발자가 구현한 서블릿들을 모은 하나의 프로젝트(etc. &lt;code&gt;war&lt;/code&gt;)를 웹 서블릿 컨테이너인 톰캣에 배포하면 톰캣은 &lt;code&gt;web.xml&lt;/code&gt; 파일을 참고하여 서블릿 컨텍스트를 생성하고 이는 웹 애플리케이션(배포한 프로젝트로 생성되는 하나의 인스턴스) 당 하나가 생성된다. 서블릿 컨텍스트는 다양한 데이터를 저장할 수 있는데, 예를들어 톰캣에서 JSP 페이지를 내려줄 때 모든 페이지(서블릿)에서 공통적으로 사용되는 HTML 타이틀, footer의 기업정보 등 서블릿들이 공통적으로 접근해서 참조할 수 있는 정보들을 저장할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x1nCM/btrnxmfhfxK/zgZ4EOqxSEIuXuGQl82P6k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x1nCM/btrnxmfhfxK/zgZ4EOqxSEIuXuGQl82P6k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x1nCM/btrnxmfhfxK/zgZ4EOqxSEIuXuGQl82P6k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx1nCM%2FbtrnxmfhfxK%2FzgZ4EOqxSEIuXuGQl82P6k%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;418&quot; height=&quot;258&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javaee/5/tutorial/doc/bnafe.html&quot;&gt;Oracle: The Java EE Tutorial - What Is a Servlet?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.javatpoint.com/life-cycle-of-a-servlet&quot;&gt;javaTpoint: Life Cycle of a Servlet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/what-servlet-container&quot;&gt;DZone: What is a Servlet Container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletContext.html&quot;&gt;tomcat document: javax.servlet.SerlvetContext interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codeofenow.tistory.com/32&quot;&gt;개발 일기장: [JSP] ServletContext(Application)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/28131102/how-servlet-container-finds-webapplicationinitializer-implementations&quot;&gt;StackOverflow: How servlet container finds WebApplicationInitializer implementations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Framework &amp;amp; Library &amp;amp; Tool/Spring&amp;amp;SpringBoot</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/46</guid>
      <comments>https://rura6502.tistory.com/entry/Servlet-Servlet-Container-Spring-Container#entry46comment</comments>
      <pubDate>Fri, 10 Dec 2021 10:14:41 +0900</pubDate>
    </item>
    <item>
      <title>프로세스, 스레드</title>
      <link>https://rura6502.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
      <description>&lt;h1&gt;프로세스, Process&lt;/h1&gt;
&lt;p&gt;실행 가능한 하나의 프로그램이 OS(Operation System)로부터 고유한 시스템 리소스 영역을 할당받아 실행 가능한 형태로 해당 영역에 적재(특히 메모리)되는 하나의 인스턴스이다. 일반적으로 프로그램을 하나 실행하면 그 프로그램이 OS가 할당한 시스템 리소스 영역에 적재되어 하나 이상의 프로세스가 실행된다. 즉 프로세스는 &lt;code&gt;현재 실행 중인 프로그램의 하나의 형태&lt;/code&gt;이라고 할 수 있다. 여기서 OS로 부터 할당받는 다는 것은 OS의 스케쥴러에 의해 영향을 받는다는 의미이다. 예를들어 필요에 따라 OS 스케쥴러가 특정 프로세스의 리소스를 멈추게하는 등의 행위를 할 수 있다.&lt;/p&gt;
&lt;p&gt;고유한 시스템 리소스 영역을 할당받기 때문에 다른 프로세스가 할당받은 영역에 접근할 수 없다. 이러한 이유로 프로세스 간 통신을 위해 IPC(Inter-Process Communication, 프로세스 간 통신) 기술이 필요하다. IPC 기술엔 소켓(http도 결국 소켓), 메세지큐, 파이프, 파일등이 있다.&lt;/p&gt;
&lt;h2&gt;PCB, Process Control Block&lt;/h2&gt;
&lt;p&gt;OS는 프로세스를 PCB라는 자료구조로 컨트롤하며 &lt;code&gt;Process Descriptor&lt;/code&gt;라고 부르기도 한다. 프로세스 하나에 하나의 PCB가 만들어진다..&lt;/p&gt;
&lt;p&gt;PCB는 크게 프로세스를 구분할 수 있는 정보, 현재 프로세스의 상태, 프로세스 제어를 위한 정보가 저장이 된다. &lt;a href=&quot;https://en.wikipedia.org/wiki/Process_control_block#Structure&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Process Memory&lt;/h2&gt;
&lt;p&gt;PCB가 가지고 있는 정보 중 &lt;code&gt;프로세스가 사용할 수 있는 메모리의 위치정보&lt;/code&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;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NjcWo/btrnnDvM3ds/4tauUyB2Iosb4SuWSy5GG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NjcWo/btrnnDvM3ds/4tauUyB2Iosb4SuWSy5GG0/img.png&quot; data-alt=&quot;Process Memory Model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NjcWo/btrnnDvM3ds/4tauUyB2Iosb4SuWSy5GG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNjcWo%2FbtrnnDvM3ds%2F4tauUyB2Iosb4SuWSy5GG0%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;261&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Process Memory Model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stack : 말 그대로 stack 형태의 자료구조를 가진 공간. 함수 호출 시 함수의 정보(매개변서, 지역변수 등)가 저장되는 영역.&lt;/li&gt;
&lt;li&gt;heap : 동적으로 생성되는 메모리 영역. 동적이란 프로세스가 실행되면서 개발자가 정의한 로직에 따라 공간이 늘어나거나 줄어들 수 있음. c언어 계열에선 &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;calloc&lt;/code&gt; 함수를 통해 생성되는 공간이 배정되는 영역&lt;/li&gt;
&lt;li&gt;data(BSS, GVAR) : 정적 데이터가 저장되는 영역. static으로 생성하는 데이터가 저장됨.&lt;/li&gt;
&lt;li&gt;code(text) : 프로그램 코드가 들어가는 영역. 읽기 전용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;메모리 공간의 사이즈는 stack, heap 영역은 프로그램 런타임 단계에서 결정되고 data, code 영역은 컴파일 단계에서 결정된다. 프로세스가 사용할 수 있는 메모리 크기, 위 그림에 해당하는 박스의 크기는 프로세스가 실행되면서 정해지고 메모리의 마지막 영역을 시작점으로 용량이 증가하는 stack영역과 code, data 영역 이후로 시작되는 heap 영역이 unused memory을 사용하며 공간을 늘여나가는 방식이다. 이는 상황에 따라 stack, heap의 사이즈가 커져 더 이상 사용할 수 있는 unused memory가 없을 경우 &amp;#39;더 이상 사용할 영역이 없음&amp;#39;이라는 오류가 나게 되는데 이 오류가 바로 &lt;code&gt;stack overflow&lt;/code&gt;, &lt;code&gt;heap overflow&lt;/code&gt; 오류이다.&lt;/p&gt;
&lt;h1&gt;쓰레드, Thread&lt;/h1&gt;
&lt;p&gt;프로세스를 구성하는 구성요소로 하나의 프로그램 안에서 실행될 수 있는 명령어들의 집합 인스턴스이다.. semi process, light weight process 등의 명칭으로도 불리며 명칭을 보더라도 프로세스보다 경량화되었음을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QCoso/btrnqyUKlDr/JlmWaDGlKEUSQ6hb0l0Bek/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QCoso/btrnqyUKlDr/JlmWaDGlKEUSQ6hb0l0Bek/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QCoso/btrnqyUKlDr/JlmWaDGlKEUSQ6hb0l0Bek/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/QCoso/btrnqyUKlDr/JlmWaDGlKEUSQ6hb0l0Bek/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;337&quot; height=&quot;259&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;쓰레드는 하나의 프로세스 내부에 생성되어 프로세스의 code, heap, stack 영역에 접근할 수 있다.(위 그림에서 왼쪽 static, heap, code 영역)&lt;/p&gt;
&lt;p&gt;스레드는 생성되면서 별도의 stack, register 공간을 할당받게 되는데 이는 스레드 함수 내부에서 선언한 변수 등이 저장되는 장소로 사용되고 프로세스는 스레드를 생성하면서 그 정보를 call stakc에 저장한다. 보통 일반적인 프로그램의 경우 실행 시 main thread가 실행의 시작점이 되는 main 함수를 실행하고 실행 흐름 또는 외부 입력에 반응하여 프로그래머가 정의한 흐름에 따라 독립적으로 실행되는 사용자 정의 스레드로 구성된다. 둘 이상의 스레드를 사용할 경우 &lt;code&gt;Multi thread, 멀티 스레드&lt;/code&gt;라고 한다.&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&quot;&gt;wiki: 프로세스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kyu9341.github.io/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C/2020/10/04/OS_Process_Structure/&quot;&gt;kwon&amp;#39;s Blog: OS-프로세스 메모리 구조&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bowbowbow.tistory.com/16&quot;&gt;bowbowbow: [운영체제] 프로세스가 뭐지?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.joinc.co.kr/w/Site/Thread/Beginning/WhatThread&quot;&gt;Joinc: 쓰레드에 대해서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>computer science &amp;amp; engineer</category>
      <author>rura6502</author>
      <guid isPermaLink="true">https://rura6502.tistory.com/45</guid>
      <comments>https://rura6502.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%8A%A4%EB%A0%88%EB%93%9C#entry45comment</comments>
      <pubDate>Thu, 9 Dec 2021 15:31:33 +0900</pubDate>
    </item>
  </channel>
</rss>