<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>절대적 발전</title>
    <link>https://lteawoo.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 10:35:23 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>일태우</managingEditor>
    <image>
      <title>절대적 발전</title>
      <url>https://tistory1.daumcdn.net/tistory/3198897/attach/1b4bf3a3400c43dabba41ef00a5b03ac</url>
      <link>https://lteawoo.tistory.com</link>
    </image>
    <item>
      <title>Json, XML, Yaml(yml) 시각화로 편하게 작업하기</title>
      <link>https://lteawoo.tistory.com/90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://structra.taeu.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://structra.taeu.kr/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774112184864&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Structra - JSON, XML &amp;amp; YAML Data Formatting and Visualization Tool&quot; data-og-description=&quot;Free online tool to format, validate, compress and visualize JSON, XML, and YAML data with tree and graph views.&quot; data-og-host=&quot;structra.taeu.kr&quot; data-og-source-url=&quot;https://structra.taeu.kr/&quot; data-og-url=&quot;https://structra.taeu.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HavJq/dJMb84p7XRT/zcjsRNsbY7Tl7pKKnBLK51/img.png?width=694&amp;amp;height=484&amp;amp;face=0_0_694_484,https://scrap.kakaocdn.net/dn/cgVKXn/dJMb87f5JQn/Pqd3gF4Eo2rTqyRs0NWhrk/img.png?width=694&amp;amp;height=484&amp;amp;face=0_0_694_484,https://scrap.kakaocdn.net/dn/JVY5L/dJMb87NVG5p/u8ACdkQxk8NRdnITYOrmuk/img.png?width=694&amp;amp;height=484&amp;amp;face=0_0_694_484&quot;&gt;&lt;a href=&quot;https://structra.taeu.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://structra.taeu.kr/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HavJq/dJMb84p7XRT/zcjsRNsbY7Tl7pKKnBLK51/img.png?width=694&amp;amp;height=484&amp;amp;face=0_0_694_484,https://scrap.kakaocdn.net/dn/cgVKXn/dJMb87f5JQn/Pqd3gF4Eo2rTqyRs0NWhrk/img.png?width=694&amp;amp;height=484&amp;amp;face=0_0_694_484,https://scrap.kakaocdn.net/dn/JVY5L/dJMb87NVG5p/u8ACdkQxk8NRdnITYOrmuk/img.png?width=694&amp;amp;height=484&amp;amp;face=0_0_694_484');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Structra - JSON, XML &amp;amp; YAML Data Formatting and Visualization Tool&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Free online tool to format, validate, compress and visualize JSON, XML, and YAML data with tree and graph views.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;structra.taeu.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;개발이나 각종 데이터 다루다보면, JSON이나 XML 데이터를 마주할 때가 많음. 이때 별도 설치 없이 브라우저에서 바로 데이터를 정렬하고 분석할 수 있는 &lt;b data-index-in-node=&quot;84&quot; data-path-to-node=&quot;3&quot;&gt;Structra&lt;/b&gt; 사이트를 소개함(내가 쓰려고 만듬)&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. 주요 기능 및 특징&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이 사이트는 단순한 포맷터를 넘어 데이터 구조를 다루는 데 필요한 핵심 기능을 모두 갖추고 있음.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;멀티 포맷 지원:&lt;/b&gt; JSON은 기본이고, XML과 YAML까지 한 곳에서 처리 가능함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;데이터 정제 (Beautify/Minify):&lt;/b&gt; 난잡한 코드를 한눈에 보기 좋게 정렬하거나, 반대로 공백을 제거해 용량을 줄이는 압축 기능을 제공함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;실시간 검증 (Validate):&lt;/b&gt; 구문 오류를 즉시 잡아내어 데이터의 무결성을 확인할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,3,0&quot;&gt;트리 뷰 시각화 (Visualize):&lt;/b&gt; 텍스트 형태의 데이터를 인터렉티브한 트리 구조로 보여주어 계층 관계 파악이 매우 쉬움.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,4,0&quot;&gt;상호 변환 (Convert):&lt;/b&gt; JSON &amp;harr; XML, JSON &amp;harr; YAML 등 포맷 간 변환을 클릭 몇 번으로 해결함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;2. 단계별 사용법&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;사용법은 매우 직관적이라 누구나 쉽게 따라 할 수 있음.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;STEP 1. 데이터 입력&lt;/b&gt; 메인 화면의 텍스트 영역에 분석하거나 변환하고 싶은 코드를 복사해서 붙여넣음.&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10&quot;&gt;STEP 2. 원하는 도구 선택&lt;/b&gt; 상단 메뉴에서 데이터 형식(JSON, XML, YAML)에 맞는 툴을 선택함. 'Format'을 누르면 즉시 예쁘게 정렬됨.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;STEP 3. 시각화 및 분석&lt;/b&gt; 'Visualize' 탭을 활용해 데이터의 계층 구조를 트리 형태로 펼쳐보며 특정 키(Key)나 값(Value)을 빠르게 찾음.&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt;STEP 4. 결과 활용&lt;/b&gt; 정돈된 데이터를 다시 복사해서 프로젝트 코드나 설정 파일에 적용하면 끝임.&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-filename=&quot;스크린샷 2026-03-22 오전 1.59.31.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;1604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfjFig/dJMcacbnFyQ/dRrtNTvZ8clvJdC9U6RpG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfjFig/dJMcacbnFyQ/dRrtNTvZ8clvJdC9U6RpG0/img.png&quot; data-alt=&quot;Json view&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfjFig/dJMcacbnFyQ/dRrtNTvZ8clvJdC9U6RpG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfjFig%2FdJMcacbnFyQ%2FdRrtNTvZ8clvJdC9U6RpG0%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;1738&quot; height=&quot;1604&quot; data-filename=&quot;스크린샷 2026-03-22 오전 1.59.31.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;1604&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Json view&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 트리뷰는 위와 같이 나옴 각 속성별로 클릭해서 볼 수 있고, 마우스를 올리거나 클릭하면 에디터 하이라이팅됨&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-filename=&quot;스크린샷 2026-03-22 오전 2.01.15.png&quot; data-origin-width=&quot;2734&quot; data-origin-height=&quot;1432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/emj21v/dJMcahX3zpF/GmruY3P9JHH2MFsCwk6YSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/emj21v/dJMcahX3zpF/GmruY3P9JHH2MFsCwk6YSk/img.png&quot; data-alt=&quot;Graph View&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/emj21v/dJMcahX3zpF/GmruY3P9JHH2MFsCwk6YSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Femj21v%2FdJMcahX3zpF%2FGmruY3P9JHH2MFsCwk6YSk%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;2734&quot; height=&quot;1432&quot; data-filename=&quot;스크린샷 2026-03-22 오전 2.01.15.png&quot; data-origin-width=&quot;2734&quot; data-origin-height=&quot;1432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Graph View&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 뷰는 위와 같이 나옴, Json이나 각종 구조화된 데이터를 연관관계를 분석해서 그래프로 표시해줌&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;난 요즘 각 서비스 환경 설정 하거나 할때 쓰는 설정파일을 편집할때 주로 쓰고, AI 가 주는 결과데이터를 분석할때도 자주씀&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-filename=&quot;스크린샷 2026-03-22 오전 2.02.42.png&quot; data-origin-width=&quot;2624&quot; data-origin-height=&quot;1464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxtIR/dJMcagY8Ejf/Dg5tLIiFm8Y2iBVyibjyyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxtIR/dJMcagY8Ejf/Dg5tLIiFm8Y2iBVyibjyyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxtIR/dJMcagY8Ejf/Dg5tLIiFm8Y2iBVyibjyyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxtIR%2FdJMcagY8Ejf%2FDg5tLIiFm8Y2iBVyibjyyk%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;2624&quot; height=&quot;1464&quot; data-filename=&quot;스크린샷 2026-03-22 오전 2.02.42.png&quot; data-origin-width=&quot;2624&quot; data-origin-height=&quot;1464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 컨버팅 기능도 지원하니까 나름 유용하게 쓸 수 있을 것 같다.&lt;/p&gt;</description>
      <category>IT Tip</category>
      <category>json</category>
      <category>XML</category>
      <category>yaml</category>
      <category>yml</category>
      <category>데이터변환</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/90</guid>
      <comments>https://lteawoo.tistory.com/90#entry90comment</comments>
      <pubDate>Sun, 22 Mar 2026 02:03:35 +0900</pubDate>
    </item>
    <item>
      <title>window 11 부팅 후 프로그램이 느리게 켜질 때</title>
      <link>https://lteawoo.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 11 부팅 후 시작프로그램이 느리게 켜질 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;윈도우 버튼 + R&lt;/li&gt;
&lt;li&gt;regedit 입력 + 엔터&lt;/li&gt;
&lt;li&gt;[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Serialize] 찾기, Serialize 없으면 키 생성&lt;/li&gt;
&lt;li&gt;Dword 추가 (값 수정 필요 없음)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;StartupDelayInMSec&lt;/li&gt;
&lt;li&gt;WaitForIdleState&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법을 잘 모르겠으면 아래 파일 실행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/bn8ug7/dJMb9MisIe0/OyoNNZdsG4y3oI4ri4mRoK/%EB%B6%80%ED%8C%85%EB%8A%90%EB%A6%B4%EB%95%8C.reg?attach=1&amp;amp;knm=tfile.reg&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;부팅느릴때.reg&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.00MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT Tip</category>
      <category>window11</category>
      <category>부팅</category>
      <category>윈도우11</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/89</guid>
      <comments>https://lteawoo.tistory.com/89#entry89comment</comments>
      <pubDate>Mon, 15 Sep 2025 03:12:14 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] build 시 apk ssl certificate verify failed, apk permission denied 발생 시</title>
      <link>https://lteawoo.tistory.com/88</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker build 시 apk ssl certificate verify failed 혹은 apk permission denied 발생&lt;/p&gt;
&lt;pre id=&quot;code_1727318043747&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tls_post_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:2091:
fetching https://dl-cdn.alpinelinux.org/alpine/v3.20/community: Permission denied&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alpine 이미지 작업에서 apk add 사용 중에 발생&lt;/p&gt;
&lt;pre id=&quot;code_1727318121994&quot; class=&quot;dockerfile&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;RUN sed 's/https/http/g' -i /etc/apk/repositories​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dockerfile에 위 명령어 추가하여 https를 http로 변경하면 정상처리&lt;/p&gt;</description>
      <category>Infrastructure</category>
      <category>alpine</category>
      <category>docker</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/88</guid>
      <comments>https://lteawoo.tistory.com/88#entry88comment</comments>
      <pubDate>Thu, 26 Sep 2024 11:35:51 +0900</pubDate>
    </item>
    <item>
      <title>헥사고날 아키텍쳐 - 도메인 헥사곤으로 비즈니스 모델링(1)</title>
      <link>https://lteawoo.tistory.com/87</link>
      <description>&lt;h1&gt;도메인 헥사곤으로 비즈니스 모델링&lt;/h1&gt;
&lt;h2&gt;엔티티를 활용한 문제영역 모델링&lt;/h2&gt;
&lt;h3&gt;도메인 엔티티의 순수성&lt;/h3&gt;
&lt;p&gt;문제영역 모델링의 주요 초점은 최대한 실제 시나리오를 코드로 변환하는 것, 비즈니스 요구사항을 이해하고 코드로 변환하는데 실패하면 결과적으로 비용의 손실이 발생할 수 밖에 없다&lt;/p&gt;
&lt;p&gt;문제영역 모델링의 핵심은 엔티티를 만드는 것이다. 엔티티가 비즈니스 요구사항과 밀접한 관계를 가져야 하기 때문에, 비즈니스 관련 코드와 기술 관련 코드가 혼동되는 것을 방지 해야한다.(기술적: 소프트웨어 맥락에서만 존재하고 의미가 있는 것)&lt;/p&gt;
&lt;p&gt;헥사고날 아키텍처는 전통적인 비즈니스 문제를 해결하려는 프로젝트에 초점이 맞춰져 있기 때문에, 새로운 개발 프레임워크와 같은 순수하게 기술적인 프로젝트에는 최선의 접근 방법이 아닐 수도 있다.&lt;/p&gt;
&lt;p&gt;도메인 엔티티는 비즈니스 관심사만 처리한다는 점에서 순수해야 한다.&lt;/p&gt;
&lt;h3&gt;적절하지 않은 엔티티 구성&lt;/h3&gt;
&lt;p&gt;비즈니스 규칙, 비즈니스 데이터 두 요소는 엔티티의 특징을 결정한다. 비즈니스 데이터만 표현하는 데이터베이스 엔티티 객체와 유사하게 모델링된 엔티티 클래스를 보는 건 드문 일이 아니다.&lt;/p&gt;
&lt;p&gt;모델링하려는 엔티티에 본질적이지 않은 로직으로 엔티티 클래스에 과부하를 주면 안된다. 이러한 오퍼레이션에 대해서는 서비스를 통해 수용할 수 있다.&lt;/p&gt;
&lt;p&gt;이전 &lt;code&gt;Router&lt;/code&gt;클래스에서는 라우터들을 필터링하고 나열하기 위해 retrieveRouter 메서드를 만들었다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static List&amp;lt;Router&amp;gt; retrieveRouter(List&amp;lt;Router&amp;gt; routers, Predicate&amp;lt;Router&amp;gt; predicate) {
    return routers.stream()
            .filter(predicate)
            .collect(Collectors.&amp;lt;Router&amp;gt;toList());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실제 세계에서 이 동작은 라우터의 본질적인 특성으로 볼 수 없다. 이는 서비스를 통해 수용해야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class RouterSearch {

    public static List&amp;lt;Router&amp;gt; retrieveRouter(List&amp;lt;Router&amp;gt; routers, Predicate&amp;lt;Router&amp;gt; predicate) {
        return routers.stream()
                .filter(predicate)
                .collect(Collectors.&amp;lt;Router&amp;gt;toList());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;도메인 서비스 클래스 &lt;code&gt;RouterSearch&lt;/code&gt;에서 이를 수용한다. 이제 &lt;code&gt;retrieveRouter&lt;/code&gt;메서드는 도메인 헥사곤과 다른 헥사곤에 있는 다양한 객체들이 서비스로 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isCore&lt;/code&gt;, &lt;code&gt;isEdge&lt;/code&gt;, &lt;code&gt;filterRouterByType&lt;/code&gt;와 같은 도메인 제약사항 메서드는 Router 엔티티 클래스에 계속 유지한다.&lt;/p&gt;
&lt;h3&gt;UUID를 이용한 식별자 정의&lt;/h3&gt;
&lt;p&gt;식별자의 중복 생성 및 방지를 위해 데이터베이스 시퀀스에 의존하는 것은 책임을 위임하는 것이며, 중요 부분이 외부와 결합하게 된다. 분리 방법 중 하나는 UUID(universally unique identifier)를 사용하는 것이다.&lt;/p&gt;
&lt;p&gt;단점으로는 시퀀스는 정수형태지만 UUID는 문자열이라 리소스를 더 소비한다. 또한 인덱스 관리에 상당한 영향을 줄 수 있다. 이처럼 기술에 구애받지 않는 것 대신 컴퓨터 리소스가 비용으로 들어간다.&lt;/p&gt;
&lt;p&gt;엔티티 ID는 한번 정의하고 나면 불변이 되어야 한다. 이러한 불변 속성은 엔티티 ID 속성을 &lt;code&gt;값 객체&lt;/code&gt;로 모델링하기에 적합한 후보로 만든다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class RouterId {

    private final UUID id;

    public RouterId(UUID id) {
        this.id = id;
    }

    public static RouterId withId(String id) {
        return new RouterId(UUID.fromString(id));
    }

    public static RouterId withoutId() {
        return new RouterId(UUID.randomUUID());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;도메인 비즈니스의 모든 것이 ID를 갖는 것은 아니므로, 풍부한 도메인 모델을 생성하기 위해서는 엔티티만으로는 충분하지 않다. ID 없는 객체를 표현하기 위한 방법으로 &lt;code&gt;값 객체&lt;/code&gt;를 이용한다.&lt;/p&gt;
&lt;h2&gt;값 객체를 통한 서술력 향상&lt;/h2&gt;
&lt;p&gt;문제 영역을 모델링하기 위해 내장 타입만 사용하는 것은 충분하지 않다. 비즈니스 본질과 목적을 정확하게 전달하기 위해 우리는 내장타입과 사용자 정의 타입 또한 &lt;code&gt;값 객체&lt;/code&gt;로 감싸야 한다.&lt;/p&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;p&gt;값 객체로 엔티티를 구성하면 좋은 예를 알아보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Event implements Comparable&amp;lt;Event&amp;gt; {
    private EventId id;
    private OffsetDateTime timestamp;
    private String protocol;
    private String activity;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;activity 필드의 내용을 보면 출발지 호스트와 목적지 호스트를 알 수 있다고 해보자. &lt;code&gt;source.1234 &amp;gt; destination.5555&lt;/code&gt; 이와 같이 표현 된다고 했을때 &lt;code&gt;&amp;gt;&lt;/code&gt; 기호로 출발지와 목적지를 구분 하고 있다.&lt;br&gt;이와 같은 방식은 클라이언트에 부담을 주게 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;var srcHost = event.getActivity().split(&amp;quot;&amp;gt;&amp;quot;)[0];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이를 값 객체로 개선 하면 해결 할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Activity {
    private String description;
    private final String srcHost;
    private final String dstHost;

    public Activity(String description) {
        this.description = description;
        String[] split = description.split(&amp;quot;&amp;gt;&amp;quot;);
        this.srcHost = split[0];
        this.dstHost = split[1];
    }

    public String retrieveSrcHost() {
        return this.srcHost;
    }
}

public class Event implements Comparable&amp;lt;Event&amp;gt; {
    private EventId id;
    private OffsetDateTime timestamp;
    private String protocol;
    private Activity activity;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그럼 클라이언트의 코드가 명확해지고 표현력도 좋아진다. 또한 임의의 데이터 처리도 필요가 없어진다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;var srcHost = event.getActivity().retrieveSrcHost();&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로그래밍</category>
      <category>java</category>
      <category>소프트웨어 아키텍쳐</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/87</guid>
      <comments>https://lteawoo.tistory.com/87#entry87comment</comments>
      <pubDate>Sun, 18 Aug 2024 21:07:21 +0900</pubDate>
    </item>
    <item>
      <title>헥사고날 아키텍쳐 - 프레임워크 헥사곤</title>
      <link>https://lteawoo.tistory.com/86</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;프레임워크 헥사곤&lt;/h2&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;드라이빙 방식(Driving): 입력 어댑터(Input Adapter)를 사용한다.&lt;/li&gt;
&lt;li&gt;드리븐 방식(Driven): 출력 어댑터(Output Adapter)를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;드라이빙 오퍼레이션과 입력 어댑터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드라이빙 오퍼레이션은 소프트웨어에 동작을 요청하는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 애플리케이션에서 사용자가 버튼 눌러 폼을 제출하는 경우&lt;/li&gt;
&lt;li&gt;외부 시스템에서 REST API로 애플리케이션에 데이터를 요청하는 경우&lt;/li&gt;
&lt;li&gt;외부 시스템에서 MQ로 메세지를 보내는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 API는 외부 엔티티가 시스템과 상호작용하고, 외부 엔티티의 요청을 도메인 애플리케이션으로 변환하는 방법을 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드라이빙이라는 단어는 외부 엔티티들이 시스템의 동작을 유도하기 때문에 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 입력 포트 오퍼레이션 중 stdin을 이용하여 호출 하는 입력 어댑터이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public class RouterViewCLIAdapter {

    private RouterViewUseCase routerViewUseCase;

    public RouterViewCLIAdapter() {
        setAdapters();
    }

    public List&amp;lt;Router&amp;gt; obtainRelatedRouters(String type) {
        return routerViewUseCase.getRouters(Router.filterRouterByType(RouterType.valueOf(type)));
    }

    private void setAdapters() {
        this.routerViewUseCase = new RouterViewInputPort(RouterViewFileAdapter.getInstance());
    }
}
&lt;/code&gt;&lt;/pre&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;명령어를 통해 obtainRelatedRouters를 호출하면 애플리케이션 유스케이스를 호출한다.&lt;/li&gt;
&lt;li&gt;입력 데이터 캡슐화: 문자열 형태의 type을 RouterType 열거형으로 변환하여 도메인 엔티티로 캡슐화 한다.&lt;/li&gt;
&lt;li&gt;도메인 로직 호출: 변환된 RouterType을 통해 도메인 제약사항을 다룬다.&lt;/li&gt;
&lt;li&gt;유스케이스 실행: 필터링된 결과를 routerViewUseCase.getRouters를 통해 비즈니스 로직을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST 와 같은 다른 통신 형식을 활성화하려면 REST 통신 엔드포인트 노출을 위한 의존성을 포함하는 새로운 REST 어댑터를 생성하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;드리븐 오퍼레이션과 출력 어댑터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;도메인 로직에서 처리된 데이터를 데이터베이스에 저장하는 경우&lt;/li&gt;
&lt;li&gt;애플리케이션이 외부 API를 호출하여 데이터를 전송하거나 요청하는 경우&lt;/li&gt;
&lt;li&gt;도메인 로직이 완료된 후 MQ에 메세지를 보내는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 애플리케이션이 오라클 기반 DB로 구성되었다가 Mongo DB로 변경한다고 해보자. 초기에는 오라클 DB와 지속성을 허용하는 출력 어댑터만 가지고 있었다. Mongo와 통신하기 위해 애플리케이션과 도메인 헥사곤은 건들면 안되고, 프레임워크 헥사곤에 출력 어댑터를 생성해야 한다. 입력 어댑터와 출력 어댑터 모두 헥사곤 내부를 가리키고 있기 때문에 이것들을 애플리케이션 및 도메인 헥사곤에 종속되게 만들어 의존성을 역전 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드리븐이라는 단어는 헥사고날 애플리케이션 자체에 의해 오퍼레이션이 유도되고 통제되며, 다른 외부 시스템에서 동작을 트리거 하기 때문이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public class RouterViewFileAdapter implements RouterViewOutputPort {

    private static RouterViewFileAdapter instance;

    @Override
    public List&amp;lt;Router&amp;gt; fetchRouters() {
        return readFileAsString();
    }

    private static List&amp;lt;Router&amp;gt; readFileAsString() {
        List&amp;lt;Router&amp;gt; routers = new ArrayList&amp;lt;&amp;gt;();
        try (Stream&amp;lt;String&amp;gt; stream = new BufferedReader(
                new InputStreamReader(RouterViewFileAdapter.class.getClassLoader().getResourceAsStream(&quot;routers.txt&quot;))).lines()) {
            stream.forEach(line -&amp;gt; {
                String[] routerEntry = line.split(&quot;;&quot;);
                var id = routerEntry[0];
                var type = routerEntry[1];
                Router router = new Router(RouterType.valueOf(type), RouterId.of(id));
                routers.add(router);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        return routers;
    }

    private RouterViewFileAdapter() {

    }

    public static RouterViewFileAdapter getInstance() {
        if (instance == null) {
            instance = new RouterViewFileAdapter();
        }
        return instance;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션이 외부 데이터를 얻는 방법으로 파일에서 얻는 방법을 출력 어댑터가 출력 포트 인터페이스를 구현하여 정의하는 코드다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 외부 입력 &amp;rarr; 입력 어댑터 &amp;rarr; 입력 포트 &amp;rarr; 유스케이스 &amp;rarr; 출력 포트 &amp;rarr; 출력 어댑터 &amp;rarr; 외부 시스템의 흐름을 살펴 보았다.&lt;/p&gt;</description>
      <category>프로그래밍</category>
      <category>java</category>
      <category>소프트웨어 아키텍쳐</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/86</guid>
      <comments>https://lteawoo.tistory.com/86#entry86comment</comments>
      <pubDate>Sun, 4 Aug 2024 21:40:01 +0900</pubDate>
    </item>
    <item>
      <title>헥사고날 아키텍쳐 - 애플리케이션 헥사곤</title>
      <link>https://lteawoo.tistory.com/85</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;애플리케이션 헥사곤&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 특화 작업을 추상적으로 처리하는 곳, 기술 관심사를 직접 다루지 않기 때문에 추상적인 것을 이야기한다. 도메인 비즈니스 규칙에 기반한 소프트웨어 사용자의 의도와 기능을 표현한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유스케이스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;자바에서는 인터페이스로 정의된 추상화로 나타낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public interface RouterViewUseCase {
    List&amp;lt;Router&amp;gt; getRouters(Predicate&amp;lt;Router&amp;gt; filter);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;입력 포트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유스케이스 인터페이스를 구현하는 것이 입력 포트의 역할 이다. 애플리케이션 수준에서 유스케이스에 직접 연결되는 컴포넌트이기 때문에 입력 포트는 도메인 용어로 소프트웨어의 의도를 구현할 수 있게 한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class RouterViewInputPort implements RouterViewUseCase {

    private final RouterViewOutputPort routerListOutputPort;

    public RouterViewInputPort(RouterViewOutputPort routerListOutputPort) {
        this.routerListOutputPort = routerListOutputPort;
    }

    @Override
    public List&amp;lt;Router&amp;gt; getRouters(Predicate&amp;lt;Router&amp;gt; filter) {
        var routers = routerListOutputPort.fetchRouters();
        return Router.retrieveRouter(routers, filter);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 유스케이스에 서술된 소프트웨어의 의도를 구현한 입력 포트다. 도메인 제약사항을 사용해 조회하고자 하는 라우터를 필터링한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출력 포트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;이 말은 특정 데이터가 DB에서 오든 파일 시스템에서 오든 신경 쓰지 않는 말이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public interface RouterViewOutputPort {
    List&amp;lt;Router&amp;gt; fetchRouters();
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로그래밍</category>
      <category>java</category>
      <category>소프트웨어 아키텍쳐</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/85</guid>
      <comments>https://lteawoo.tistory.com/85#entry85comment</comments>
      <pubDate>Sun, 4 Aug 2024 20:45:08 +0900</pubDate>
    </item>
    <item>
      <title>헥사고날 아키텍쳐 - 도메인 헥사곤</title>
      <link>https://lteawoo.tistory.com/84</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;도메인 헥사곤&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;문제 영역이 존재하고 도메인 전문가들이 분명하게 알고 있는 상황에서 문제 영역을 엔티티, 값 객체로의 변환 방법을 파악하지 못하면 잘못된 가정을 기반으로 구축되게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 점은 소프트웨어가 간단하게 시작해서, 기반 코드가 커짐에 따라 기술 부채가 누적되고 유지 보수가 더 어려워지는 이유 중 하나로 여겨진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엔티티&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;식별자 (Identifier)&lt;/b&gt;: 엔티티는 고유한 식별자를 가지고 있으며, 이 식별자를 통해 다른 엔티티와 구분된다. 동일한 엔티티는 시간이 지나도 동일한 식별자를 유지한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변경 가능성 (Mutability)&lt;/b&gt;: 엔티티는 시간이 지남에 따라 상태가 변할 수 있다. 이는 엔티티의 특성이 변할 수 있다는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생명 주기 (Lifecycle)&lt;/b&gt;: 엔티티는 생성되고, 변경되며, 소멸되는 생명 주기를 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예) 사용자 엔티티는 사용자 ID와 같은 고유 식별자를 가지며, 이름, 이메일과 같은 변하는 속성을 지닌다. 시간이 지남에 따라 사용자의 특성이 변할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;값 객체&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 정체성보다 속성에 관심을 갖는 경우 코드의 표현력을 보완하는데 도움을 준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;식별자가 없음 (No Identifier)&lt;/b&gt;: 값 객체는 고유한 식별자를 가지지 않는다. 자체 속성 값으로 동일성 판단을 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;불변성 (Immutability)&lt;/b&gt;: 값 객체를 사용해 엔티티를 구성할 수 있으므로 도메인 전체에서 예상치 못한 불일치를 방지 하기 위해 불변으로 해야한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동등성 (Equality by Value)&lt;/b&gt;: 두 값 객체가 동일한 속성 값을 가지면, 두 값 객체는 동일한 것으로 간주한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍</category>
      <category>java</category>
      <category>소프트웨어 아키텍쳐</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/84</guid>
      <comments>https://lteawoo.tistory.com/84#entry84comment</comments>
      <pubDate>Sun, 4 Aug 2024 20:18:57 +0900</pubDate>
    </item>
    <item>
      <title>헥사고날 아키텍쳐</title>
      <link>https://lteawoo.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;포트와 어댑터 아키텍쳐라고도 불림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBiFyz/btsITviFdX7/HzTN1EWDZBi4jzuTmoFUL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBiFyz/btsITviFdX7/HzTN1EWDZBi4jzuTmoFUL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBiFyz/btsITviFdX7/HzTN1EWDZBi4jzuTmoFUL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBiFyz%2FbtsITviFdX7%2FHzTN1EWDZBi4jzuTmoFUL0%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;700&quot; height=&quot;477&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메인 아이디어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비즈니스 코드를 기술 코드로부터 분리하는 것&lt;/li&gt;
&lt;li&gt;비즈니스 코드에 피해를 주지 않고 기술 코드 변경이 가능해야 한다&lt;/li&gt;
&lt;li&gt;핵심 도메인 로직을 외부 인터페이스(입출력, DB, 사용자 인터페이스 등)으로 부터 분리함으로 달성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도메인 헥사곤&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어가 해결하기를 원하는 핵심 문제를 설명하는 요소의 결합, 이러한 요소들은 기술에 구애 받지 않는 형태로 표현되어야 함&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구성 요소
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔티티 - 식별자를 할당할 수 있는 것&lt;/li&gt;
&lt;li&gt;값 객체 - 엔티티들을 합성하기 위한 불변 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;애플리케이션 헥사곤&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 헥사곤에서 나오는 비즈니스 규칙을 사용, 처리, 조정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 측면과 기술 측면 사이에서 양쪽과 상호작용하는 중개자 역할&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프레임워크 헥사곤&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 기능의 노출 방법을 결정, 예를 들면 REST, gRPC 엔드포인트 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍</category>
      <category>java</category>
      <category>소프트웨어 아키텍쳐</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/83</guid>
      <comments>https://lteawoo.tistory.com/83#entry83comment</comments>
      <pubDate>Sun, 4 Aug 2024 20:17:49 +0900</pubDate>
    </item>
    <item>
      <title>@EnableScheduling 설정하지 않았는데 Schedule 동작 시</title>
      <link>https://lteawoo.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;@EnableScheduling을 설정하지 않았는데도 Schedule이 동작하고 있을 때 한번 확인해보세요.&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;전체적으로 라이브러리를 훑어봤는데 Redis Config부분에서 발견되었습니다.&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;spring-session-data-redis 사용하고, Redis 활성화 시에 RedisHttpSessionConfiguration를 사용하게되는데&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;아래와 같이 @EnableScheduling이 적용되어 있습니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
       implements EmbeddedValueResolverAware, ImportAware {

    private Integer maxInactiveIntervalInSeconds = 1800;

    private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();

    private String redisNamespace = &quot;&quot;;

    private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAV&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 해보세요&lt;/p&gt;</description>
      <category>Spring</category>
      <category>redis</category>
      <category>Schedule</category>
      <category>Spring</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/82</guid>
      <comments>https://lteawoo.tistory.com/82#entry82comment</comments>
      <pubDate>Wed, 17 Jan 2024 16:33:04 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 2. 컨테이너 관리</title>
      <link>https://lteawoo.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨테이너 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 실행 명령어는 다음과 같다&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;옵션&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-d --detach&lt;/td&gt;
&lt;td&gt;Detached mode(백그라운드 모드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-p --publish&lt;/td&gt;
&lt;td&gt;호스트와 컨테이너의 포트 연결(포트 포워딩)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-v --volume&lt;/td&gt;
&lt;td&gt;호스트와 컨테이너의 디렉토리 연결(파일을 호스트에 바로 저장, 마운트개념)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--rm&lt;/td&gt;
&lt;td&gt;프로세스 종료 시 컨테이너 자동 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--name&lt;/td&gt;
&lt;td&gt;컨테이너 이름 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-i --interactive&lt;/td&gt;
&lt;td&gt;표준 입력(stdin)을 활성화, bash 사용시 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-t --tty&lt;/td&gt;
&lt;td&gt;TTY 모드를 사용, bash 사용시 사용, 해당 명령을 사용하지 않으면 명령은 할 수 있겠지만 쉘이 표시되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ubuntu 16.04 컨테이너 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu 16.04 이미지 이름을 입력하여 컨테이너 실행, 로컬에 저장되어 있는지 확인하고 없으면 다운로드(pull) 한다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;docker run ubuntu:16.04&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별다른 명령을 안했기 때문에 실행중인 프로세스가 없는 ubuntu 컨테이너는 자동 종료된다. 이번에는 &lt;code&gt;/bin/bash&lt;/code&gt;를 실행 해보자&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;docker run -it --rm ubuntu:16.04 /bin/bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 바로 실행되고 bash를 사용할 수 있게 된다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;cat /etc/issue
Ubuntu 16.04.7 LTS \n \l

ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  
opt  proc  root  run  sbin  srv  sys  tmp  usr  var&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 실행 시 컨테이너 내부에서 명령어를 실행하기 위해 bash 쉘을 실행했고, 입력을 위해 &lt;code&gt;-it&lt;/code&gt;옵션을 추가, 종료시 컨테이너 제거를 위해 &lt;code&gt;--rm&lt;/code&gt;옵션을 추가하였다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cat /etc/issue&lt;/code&gt;와 &lt;code&gt;ls&lt;/code&gt;로 ubuntu환경인 것을 확인 가능하다. &lt;code&gt;exit&lt;/code&gt;명령어로 종료와 함께 컨테이너 제거가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Detached 모드로 Redis 컨테이너 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-d 로 Detached 모드로 실행하고, -p 모드로 redis port 6379를 호스트에서 1234로 포워딩하여 실행해보고 &lt;code&gt;exec&lt;/code&gt; 명령어로 redis 컨테이너에서 redis-cli를 실행해보자&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;docker run -d --name redis-test -p 1234:6379 redis

docker exec -it redis-test redis-cli

# in redis-cli
127.0.0.1:6379&amp;gt; keys *
(empty array)
127.0.0.1:6379&amp;gt; set test 1234
OK
127.0.0.1:6379&amp;gt; keys *
1) &quot;test&quot;
127.0.0.1:6379&amp;gt; get test
&quot;1234&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redis-cli로 접근하여 키를 저장해보고 불러보았다 매우 쉽게 redis를 사용해 볼 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MySQL 5.7 컨테이너 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hub.docker.com/_/mysql/&quot;&gt;https://hub.docker.com/_/mysql&lt;/a&gt;에 들어가면 환경변수에 관한 설명을 참고할 수 있다. 환경변수는 &lt;code&gt;-e&lt;/code&gt; 옵션으로 설정한다. &lt;code&gt;--name&lt;/code&gt; 옵션으로 컨테이너 이름을 설정하면 다른 명령어에서 쉽게 사용가능하다&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true --name mysql mysql:5.7&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;--name&lt;/code&gt; 옵션을 생략하면 자동으로 컨테이너이름이 생성되는데, 유명한 과학자나 해커의 이름과 수식어를 조합하여 랜덤으로 &lt;a href=&quot;https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go&quot;&gt;생성&lt;/a&gt;한다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 생성 후 DB 툴로 접속해보자 주소는 &lt;code&gt;localhost:3306&lt;/code&gt; &lt;code&gt;root&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;510&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7vYHh/btrE3qTB1vZ/nMjdPf07TTNC5BEULLZWnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7vYHh/btrE3qTB1vZ/nMjdPf07TTNC5BEULLZWnK/img.png&quot; data-alt=&quot;mysql 컨테이너에 정상 접속&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7vYHh/btrE3qTB1vZ/nMjdPf07TTNC5BEULLZWnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7vYHh%2FbtrE3qTB1vZ%2FnMjdPf07TTNC5BEULLZWnK%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;510&quot; height=&quot;354&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mysql 컨테이너에 정상 접속&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 기본 명령어&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ps&lt;/b&gt; - 컨테이너 목록 확인하기&lt;/p&gt;
&lt;pre id=&quot;code_1655454723286&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker ps [OPTIONS]

CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                               NAMES
cb1c3db80d42   mysql:5.7   &quot;docker-entrypoint.s&amp;hellip;&quot;   13 minutes ago   Up 13 minutes   0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp   mysql
ed9e6c4c1775   redis       &quot;docker-entrypoint.s&amp;hellip;&quot;   35 minutes ago   Up 35 minutes   0.0.0.0:1234-&amp;gt;6379/tcp              redis-test

docker ps -a

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS                                       NAMES
cb1c3db80d42   mysql:5.7      &quot;docker-entrypoint.s&amp;hellip;&quot;   15 minutes ago   Up 15 minutes               0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp           mysql
ed9e6c4c1775   redis          &quot;docker-entrypoint.s&amp;hellip;&quot;   37 minutes ago   Up 37 minutes               0.0.0.0:1234-&amp;gt;6379/tcp                      redis-test
55de267345a0   ubuntu:16.04   &quot;/bin/bash&quot;              2 hours ago      Exited (0) 2 hours ago                                                  nice_ardinghelli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션 -a를 사용하면 삭제를 한건 아니기 때문에 종료된 컨테이너도 보인다.&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;&lt;b&gt;stop&amp;nbsp;&lt;/b&gt;- 컨테이너 중지하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중인 컨테이너를 중지한다&lt;/p&gt;
&lt;pre id=&quot;code_1655455012206&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker stop cb1c3db80d42 # 정지할 컨테이너 id 입력

docker ps -a

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS                                       NAMES
cb1c3db80d42   mysql:5.7      &quot;docker-entrypoint.s&amp;hellip;&quot;   20 minutes ago   Exited (0) 3 seconds ago                                                mysql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 컨테이너를 정지해보기 위해 &lt;code&gt;docker ps&lt;/code&gt; 명령어로 mysql 컨테이너의 id를 &lt;code&gt;stop&lt;/code&gt; 명령어에 입력하자. 참고로 컨테이너 id는 겹치지 않으면 앞의 2자리~4자리만 입력하여도 된다. 해당 컨테이너를 정지하고 다시 &lt;code&gt;ps -a&lt;/code&gt; 해보면 정지된 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;rm&amp;nbsp;&lt;/b&gt;- 컨테이너 제거하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료된 컨테이너를 완전히 제거한다&lt;/p&gt;
&lt;pre id=&quot;code_1655455860618&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker rm [OPTIONS] CONTAINER [CONTAINER...]

docker ps -a

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS                                       NAMES
cb1c3db80d42   mysql:5.7      &quot;docker-entrypoint.s&amp;hellip;&quot;   32 minutes ago   Exited (0) 12 minutes ago                                               mysql
ed9e6c4c1775   redis          &quot;docker-entrypoint.s&amp;hellip;&quot;   54 minutes ago   Up 54 minutes               0.0.0.0:1234-&amp;gt;6379/tcp                      redis-test
55de267345a0   ubuntu:16.04   &quot;/bin/bash&quot;              3 hours ago      Exited (0) 3 hours ago                                                  nice_ardinghelli

docker rm 55de cb1c&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql과 ubuntu 컨테이너를 삭제 해보았다. 이후 &lt;code&gt;ps -a&lt;/code&gt;로 확인 해보면 삭제된 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;images&amp;nbsp;&lt;/b&gt;- 이미지 목록 확인하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커가 다운로드한 이미지 목록을 확인한다&lt;/p&gt;
&lt;pre id=&quot;code_1655455235348&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker images [OPTIONS] [REPOSITORY[:TAG]]

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
redis        latest    604d80444252   3 days ago     117MB
mysql        5.7       2a0961b7de03   2 weeks ago    462MB
ubuntu       16.04     b6f507652425   9 months ago   135MB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 목록과 태크, ID, 생성시점, 용량을 확인 가능하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pull&lt;/b&gt; - 이미지 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 다운로드한다&lt;/p&gt;
&lt;pre id=&quot;code_1655455375481&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker pull [OPTIONS] NAME[:TAG|@DIGEST]

docker pull ubuntu:16:04&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;rmi&lt;/b&gt; - 이미지 삭제하기&lt;/p&gt;
&lt;pre id=&quot;code_1655455439370&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker rmi [OPTIONS] IMAGE [IMAGE...]

docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
redis        latest    604d80444252   3 days ago     117MB
mysql        5.7       2a0961b7de03   2 weeks ago    462MB

docker rmi 2a09

Untagged: mysql:5.7
Untagged: mysql@sha256:7e99b2b8d5bca914ef31059858210f57b009c40375d647f0d4d65ecd01d6b1d5
Deleted: sha256:2a0961b7de03c7b11afd13fec09d0d30992b6e0b4f947a4aba4273723778ed95
Deleted: sha256:2fbd454d39f146da1c0747174089b983c1cf1da7a062384e5a71a94f540141ab
Deleted: sha256:60ce6f6a8d71e66bd7ca1dae1f84a5faedfb993c1a0f352b90597b73eb94d56e
Deleted: sha256:226a653f6ee6b0184d7a6304f29976dc89b0e54e2eec2027d0e3cf87cbe8f2af
Deleted: sha256:218468cc1e9e25a831221617115da837798856793eb7decb3b089c8a1b5bd98f
Deleted: sha256:aea4fcaf4a44b4ce254718a5699557d5a278182a060683e9fcab117281c710c5
Deleted: sha256:87e7690acc799fa14efd95056b81ca599d7587771add57bb73f9066e3a1a1d10
Deleted: sha256:efb1655b4e285047d5fc78392e33a2f0b4156997e69b0833a6b78774674085d6
Deleted: sha256:fabb64857683e1f35472debb5a12a6bf2e081b5766c69874c0528086737434fb
Deleted: sha256:cf7b1ec7aec8704bd8369c4307ecf4931b0afd2d3f912c0a6066c2ac6443ac28
Deleted: sha256:6c9d31c0820816a60a4320711febf8710a1d053f1f3a424e1f071b65263da7d1
Deleted: sha256:10e6bc6fdee2d9a0d1ac5e78a031b5670f37d75599ba609cbd1d027af78f7bf4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중인 컨테이너는 이미지의 레이어기반으로 실행되므로 삭제할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨테이너 둘러보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;logs&lt;/b&gt; - 컨테이너 로그보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너의 로그를 확인한다&lt;/p&gt;
&lt;pre id=&quot;code_1655456064539&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker logs [OPTIONS] CONTAINER

docker logs redis-test
1:C 17 Jun 2022 07:56:23.572 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 17 Jun 2022 07:56:23.572 # Redis version=7.0.2, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 17 Jun 2022 07:56:23.572 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 17 Jun 2022 07:56:23.572 * monotonic clock: POSIX clock_gettime
1:M 17 Jun 2022 07:56:23.573 * Running mode=standalone, port=6379.
1:M 17 Jun 2022 07:56:23.573 # Server initialized
1:M 17 Jun 2022 07:56:23.573 * Ready to accept connections&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-tail&lt;/code&gt; 옵션으로 출력 라인 수를 지정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1655456117298&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker logs --tail 5 redis-test
1:C 17 Jun 2022 07:56:23.572 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 17 Jun 2022 07:56:23.572 * monotonic clock: POSIX clock_gettime
1:M 17 Jun 2022 07:56:23.573 * Running mode=standalone, port=6379.
1:M 17 Jun 2022 07:56:23.573 # Server initialized
1:M 17 Jun 2022 07:56:23.573 * Ready to accept connections&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-f&lt;/code&gt; 옵션으로 실시간으로 로그를 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;exec&lt;/b&gt; - 컨테이너 명령어 실행하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 관리하다보면 컨테이너에 접속하여 컨테이너 파일을 실행하고 싶어질 때가 있다. SSH는 권장되지 않는다고 하니 &lt;code&gt;exec&lt;/code&gt;명령어를 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1655456962738&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

docker exec -it mysql /bin/bash
root@6fe9822cf342:/# mysql -uroot
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.38 MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql&amp;gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

mysql&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-it&lt;/code&gt; 옵션은 키보드 입력을 위해 주었고, bash 쉘로 접속하여 작업을 진행하였다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨테이너 업데이트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 새로운 버전으로 업데이트 하는 방법은&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;새 버전 이미지를 다운로드 (pull)&lt;/li&gt;
&lt;li&gt;기존 컨테이너 삭제 (stop, rm)&lt;/li&gt;
&lt;li&gt;새 컨테이너 생성(run)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정으로 업데이트 하면 된다. 하지만 mysql 컨테이너를 이렇게 업데이트하면 데이터가 날아갈 것이다. 이를 위해 컨테이너 삭제시 유지해야하는 데이터는 컨테이너 내부가 아닌 외부에 저장하여야 한다.&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;가장 좋은 방법은 클라우드 서비스(AWS S3 등)을 이용하는 것이고, 데이터 볼륨을 추가하여 호스트의 디렉토리를 마운트해서 사용하는 방법이 있다. run 옵션중 &lt;code&gt;-v&lt;/code&gt; 옵션으로 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1655457969551&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#before
docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true --name mysql mysql:5.72

#after
docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true --name mysql -v c:\test:/var/lib/mysql mysql:5.7

/c/test
$ ls -al
total 188582
drwxr-xr-x 1 twlee 197121        0  6월 17 18:25 ./
drwxr-xr-x 1 twlee 197121        0  6월 17 18:17 ../
-rw-r--r-- 1 twlee 197121       56  6월 17 18:25 auto.cnf
-rw-r--r-- 1 twlee 197121     1112  6월 17 18:25 ca.pem
-rw-r--r-- 1 twlee 197121     1680  6월 17 18:25 ca-key.pem
-rw-r--r-- 1 twlee 197121     1112  6월 17 18:25 client-cert.pem
-rw-r--r-- 1 twlee 197121     1676  6월 17 18:25 client-key.pem
-rw-r--r-- 1 twlee 197121     1352  6월 17 18:25 ib_buffer_pool
-rw-r--r-- 1 twlee 197121 50331648  6월 17 18:25 ib_logfile0
-rw-r--r-- 1 twlee 197121 50331648  6월 17 18:25 ib_logfile1
-rw-r--r-- 1 twlee 197121 79691776  6월 17 18:25 ibdata1
-rw-r--r-- 1 twlee 197121 12582912  6월 17 18:25 ibtmp1
drwxr-xr-x 1 twlee 197121        0  6월 17 18:25 mysql/
drwxr-xr-x 1 twlee 197121        0  6월 17 18:25 performance_schema/
-rw-r--r-- 1 twlee 197121     1676  6월 17 18:25 private_key.pem
-rw-r--r-- 1 twlee 197121      452  6월 17 18:25 public_key.pem
-rw-r--r-- 1 twlee 197121     1112  6월 17 18:25 server-cert.pem
-rw-r--r-- 1 twlee 197121     1676  6월 17 18:25 server-key.pem
drwxr-xr-x 1 twlee 197121        0  6월 17 18:25 sys/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자의 환경에 맞춰 디렉토리를 변경 해주면 해당 디렉토리로 마운트 되어 파일을 확인 할 수 있다. 이제 컨테이너를 삭제해도 해당 파일은 삭제되지 않으니 컨테이너를 업데이트해도 디렉토리 마운트 후 기존 데이터 그대로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/run/&quot;&gt;https://docs.docker.com/engine/reference/run/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://subicura.com/2017/01/19/docker-guide-for-beginners-2.html&quot;&gt;https://subicura.com/2017/01/19/docker-guide-for-beginners-2.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>Infrastructure</category>
      <category>docker</category>
      <author>일태우</author>
      <guid isPermaLink="true">https://lteawoo.tistory.com/81</guid>
      <comments>https://lteawoo.tistory.com/81#entry81comment</comments>
      <pubDate>Fri, 17 Jun 2022 15:32:38 +0900</pubDate>
    </item>
  </channel>
</rss>