<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>어제보다 더 나은 개발자 되기</title>
    <link>https://hankkuu.tistory.com/</link>
    <description>3번째로 블로그 도전 </description>
    <language>ko</language>
    <pubDate>Fri, 17 Apr 2026 00:34:03 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>hankkuu</managingEditor>
    <item>
      <title>내가 보려고 작성한 맥북 세팅하기</title>
      <link>https://hankkuu.tistory.com/122</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apple Silicon 칩을 기준으로 합니다&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICloud 설정은 일단 Skip 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Software Update 는 가장 먼저 하고 시작합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Touch ID 도 미리 등록합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Homebrew 도 가장 먼저 하고 시작합니다 (Xcode Command Line Tools 도 같이 설치되야 합니다)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시스템 설정&lt;/h2&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;h4 data-ke-size=&quot;size20&quot;&gt;키보드 단축키&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능키 - F1, F2 등의 표준 기능 키로 사용 - ON&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;텍스트 입력 / 입력 소스 / 편집&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞춤법 자동 수정 - OFF&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 단어를 대문자로 시작 - OFF&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인라인 자동 완성 텍스트 보기 - OFF&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스페이스를 두 번 눌러 마침표 추가 - OFF&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스마트 인용 부호 및 대시 사용 - OFF&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;손쉬운 사용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;포인터 제어 / 트랙패드 옵션&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드래그에 트랙패드 사용 - ON&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드래그 스타일 - 세 손가락으로 드래그하기&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;디스플레이&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인터 / 마우스 포인터를 흔들어 찾기 - ON&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인터 크기 - Up&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데스크탑 및 Dock&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 Dock 가리기와 보기 - ON&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;h4 data-ke-size=&quot;size20&quot;&gt;날짜 및 시간&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;24시간제 - ON&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠금 화면에서 24시간제 보기 - ON&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;언어 및 지역&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선호하는 지역 - 영어 or 한국어&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정보&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 - 짧고 유니크 하는게 좋음 iCloud 와 local hostname 으로 쓰임&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;화면모드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다크 모드 - ON&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제어 센터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메뉴 막대에서 보기 - Wi-Fi, Bluetooth, 화면 미러링, 사운드&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배터리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메뉴 막대에서 보기 - ON&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼센트 보기 - ON&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메뉴 막대판&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계 / 시계 옵션&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요일 보기 - ON&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간에 초를 표시 - ON&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데스크탑 및 Dock&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 Dock 가리기와 보기 - ON&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;핫 코너&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 위 - 데스크탑&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오른쪽 위 - Mission Control&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;p data-ke-size=&quot;size16&quot;&gt;탭하여 클릭하기 - ON&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;잠금 화면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비활성 상태인 경우 화면 보호기 시작 - 5분 이후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배터리 사용 시 비활성 상태인 경우 디스플레이 끄기 - 5분 이후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전원 어댑터 사용 시 비활성 상태인 경우 디스플레이 끄기 - 10분 이후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 보호기 시작 후 또는 디스플레이가 꺼진 후 암호 요구 - 5분 후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠겨 있을 때 메시지 보기 - 전화번호로 설정&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Finder&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;일반&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Finder 윈도우에서 보기 - 홈 화면으로 지정&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;사이드바&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즐겨찾기 / 위치 - 필요한 것 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태크 - 불필요하면 해제&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;고급&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 파일 확장자 보기&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴더 우선 정렬 / 윈도우에서(이름순으로 정렬 시), 데스크탑에서 - Check&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;경로 막대 보기 - ON&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 막대 보기 - ON&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;보기 옵션 (home folder 에서 지정해서 하위 폴더까지 지정)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성일 - Check&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값으로 사용 - Click&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제어 센터 (필요한걸 알아서 Widget 으로 배치)&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;p data-ke-size=&quot;size16&quot;&gt;알림&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위 내용에 추가하고 싶은 것을 몇개 더 보려면 아래 내용을 참고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://subicura.com/mac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://subicura.com/mac/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1764604661600&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;macOS 안내서&quot; data-og-description=&quot;초보를 위한 macOS 안내서&quot; data-og-host=&quot;subicura.com&quot; data-og-source-url=&quot;https://subicura.com/mac/&quot; data-og-url=&quot;https://subicura.com/mac/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cCsIKs/hyZOtCaqfk/y79iqd20INezwOCmdIpuKK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/y3s6j/hyZO5TXhiY/R2vINOAnMyCBFaG7tfJUJ0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://subicura.com/mac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://subicura.com/mac/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cCsIKs/hyZOtCaqfk/y79iqd20INezwOCmdIpuKK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/y3s6j/hyZO5TXhiY/R2vINOAnMyCBFaG7tfJUJ0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;macOS 안내서&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;초보를 위한 macOS 안내서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;subicura.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Iterm2 (Terminal 설정)&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;brew&amp;nbsp;install&amp;nbsp;--cask&amp;nbsp;iterm2&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;oh-my-zsh&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zsh-syntax-hightlighting&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;powerlevel10k&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 경로 보기&lt;/li&gt;
&lt;li&gt;Git 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSH&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Git Config&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 프로그램 설치&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Homebrew Install&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Asdf&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;node&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sdkman&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JDK&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini-cli&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude-code&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Telnet&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saml2aws&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Awscli&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neovim&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #2c3e50; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Homebrew Install --cask&lt;/h4&gt;
&lt;p id=&quot;openinterminal&quot; style=&quot;background-color: #ffffff; color: #2c3e50; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenInTerminal&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon-Q&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker-Desktop&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jetbrains-toobox&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rectangle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chatgpt&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deaber-community&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intellij-idea&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual-studio-code&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keka&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maccy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Meld&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Descktop App&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chrome&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Runcat&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion Calendar&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Figma&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zoom&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Monosnap&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logi Option+&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IINA (동영상 플레이어)&lt;/p&gt;</description>
      <category>My Work/파편 조각</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/122</guid>
      <comments>https://hankkuu.tistory.com/122#entry122comment</comments>
      <pubDate>Tue, 2 Dec 2025 04:07:19 +0900</pubDate>
    </item>
    <item>
      <title>Embulk + Digdag으로 Data Pipeline 쉽게 해보기</title>
      <link>https://hankkuu.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Embulk 는&lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;대용량 ETL 작업을 위해 사용하는 오픈소스 솔루션&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;span&gt;입니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;특징&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;플러그인 형태로 여러개의 소스와 타겟을 지원합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Maven 및 Ruby gem 리포지토리에서 릴리스된 플러그인&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;스키마 예측 (Schema guessing)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;입력 데이터를 보고 자동으로 입력 데이터의 스키마(테이블 구조)를 예측합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;일일이 설정을 하려면 귀찮은 일인데 자동으로 스키마를 인식해주어서 설정양을 줄여줍니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;빅 데이터 세트를 처리하기 위한 병렬 실행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;All or Nothing 보장하는 트랜잭션 제어&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;왜 필요한가?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이타 분석에 있어서 아키텍쳐적으로 중요한 모듈중의 하나는 여러 서버로 부터 생성되는 데이타를 어떻게 모을 것인가 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Embulk는 대량 데이터 로더입니다. databases, storages, file formats, cloud services 등의 유형 간의 데이터 전송을 돕습니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;ex: Bigquery, Oracle, MySQL, PostgreSQL, CSV, JSON&amp;hellip;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;어떤 개발자가 만들기 시작했을까요?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NzUmd/btsQ6eTORbv/3p3zXMQQ9MN0V6Nsr4SCm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NzUmd/btsQ6eTORbv/3p3zXMQQ9MN0V6Nsr4SCm0/img.jpg&quot; data-alt=&quot;일본회사에서 시작한 오픈소스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NzUmd/btsQ6eTORbv/3p3zXMQQ9MN0V6Nsr4SCm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNzUmd%2FbtsQ6eTORbv%2F3p3zXMQQ9MN0V6Nsr4SCm0%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;644&quot; height=&quot;442&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일본회사에서 시작한 오픈소스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;구조로 표현하면 아래와 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMzCGu/btsQ5V73yDI/j5FT5guMiwZzipJKQnjAJ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMzCGu/btsQ5V73yDI/j5FT5guMiwZzipJKQnjAJ1/img.jpg&quot; data-alt=&quot;Embulk 동작 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMzCGu/btsQ5V73yDI/j5FT5guMiwZzipJKQnjAJ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMzCGu%2FbtsQ5V73yDI%2Fj5FT5guMiwZzipJKQnjAJ1%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;706&quot; height=&quot;519&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Embulk 동작 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE7a5L/btsQ6HWcHp8/8xREvQUdXrBS8LEB2ESTYK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE7a5L/btsQ6HWcHp8/8xREvQUdXrBS8LEB2ESTYK/img.jpg&quot; data-alt=&quot;Embulk 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE7a5L/btsQ6HWcHp8/8xREvQUdXrBS8LEB2ESTYK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE7a5L%2FbtsQ6HWcHp8%2F8xREvQUdXrBS8LEB2ESTYK%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;699&quot; height=&quot;493&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Embulk 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;ETL 이란? (Extract, Transform, Load)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;추출(Extract), 변환(Transform), 적재(Load)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;예시)&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;calendar라는 테이블에 년/월/일/시/분/초 형태로 가 컬럼이 존재합니다. 이러한 데이터를 사용해서 통계를 내는 어떤 프로그램을 실행하려고 확인 했더니 해당 프로그램은 년월일/시분초 와 같은 컬럼형태를 요구하고 있을 때&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래와 같은 작업을 하는 경우를 ETL이라고 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1. 기존 테이블의 데이터 추출(Extract)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;대상이 되는 calendar 테이블에서 년/월/일/시/분/초 형태의 데이터를 전부 추출&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2. 추출한 데이터의 변환 (Transform)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;추출한 데이터를 요구하는 형태인 년월일/시분초 형태로 변경&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;3. 추출 및 변환한 데이터의 적재(Load)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;변경이 된 데이터를 새로운 테이블에 적재&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;간단하게 정의 해보자면 &amp;ldquo;한 곳에 저장된 데이터를 필요에 의해 다른 곳으로 이동하는 것&amp;rdquo;, &quot;저장된 데이터를 변형하여(요구사항에 맞게) 다른 곳으로이동하는 것&quot; 입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://itpassion.tistory.com/entry/ETL%EC%9D%B4%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;출처&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4bZEd/btsQ5XSnoCF/k3wX95I67g2KVaWjzv91SK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4bZEd/btsQ5XSnoCF/k3wX95I67g2KVaWjzv91SK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4bZEd/btsQ5XSnoCF/k3wX95I67g2KVaWjzv91SK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4bZEd%2FbtsQ5XSnoCF%2Fk3wX95I67g2KVaWjzv91SK%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;731&quot; height=&quot;235&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;ETL 구조&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Source - Target 구조&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKRmsU/dJMb9LRvNsY/kb0KLqp9cjdShZWfQebm90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKRmsU/dJMb9LRvNsY/kb0KLqp9cjdShZWfQebm90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKRmsU/dJMb9LRvNsY/kb0KLqp9cjdShZWfQebm90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKRmsU%2FdJMb9LRvNsY%2Fkb0KLqp9cjdShZWfQebm90%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;1030&quot; height=&quot;524&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;
&lt;div&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;소스와 타겟으로 각각의 시스템 단위로 데이터를 이관하고 있습니다&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Embulk 로는 이관하는 것이 가능하지만 모든 Job 들을 관리하는 것은 되지 않습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 부분을 보완해주는 툴로는 Digdag 을 사용하고 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Data Workflow Management Tool을 쓰는 이유는?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터를 추출, 가공, 저장 하는 ETL(Extract, Transform, Load)등을 진행하다보면 여러개의 일들이 연결되어 수행되는 경우가 필연적으로 발생됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여러개의 일들이 연결되어 수행 하는 동작하는 동작 흐름(Workflow)을 실행시키기 위해 배치 형태의 테스트를 실행 , 에러 분기에 따른 알림 , 재실행을 시켜주는 도구의 요구사항이 생기게 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이러한 이유들로 인해 Data Workflow를 편리하게 실행시키고 관리하는 자동화 도구로 Airflow , Luigi , Digdag , Oozie)들이 등장하게 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHgZ2F/btsQ64iU5Hs/aEuZj5w2TK9DBocVaqDbO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHgZ2F/btsQ64iU5Hs/aEuZj5w2TK9DBocVaqDbO1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHgZ2F/btsQ64iU5Hs/aEuZj5w2TK9DBocVaqDbO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHgZ2F%2FbtsQ64iU5Hs%2FaEuZj5w2TK9DBocVaqDbO1%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;428&quot; height=&quot;337&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오픈소스 workflow 관리 도구들&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;선언형: XML이나 YAML과 같은 서식으로 기술&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;스크립트형: 예를 들면 파이썬과 같은 스크립트 언어로 기술한다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Digdag 란?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Digdag는 복잡한 작업 파이프라인을 구축, 실행, 예약 및 모니터링하는 데 도움이 되는 간단한 도구입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;태스크가 순서대로 또는 병렬로 실행되도록 종속성 해결을 처리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcxMXB/btsQ5SDxulZ/LNMBP7zVywGZco1tnxkPzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcxMXB/btsQ5SDxulZ/LNMBP7zVywGZco1tnxkPzK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcxMXB/btsQ5SDxulZ/LNMBP7zVywGZco1tnxkPzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcxMXB%2FbtsQ5SDxulZ%2FLNMBP7zVywGZco1tnxkPzK%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;1280&quot; height=&quot;620&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;digdag workflow ui&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;진짜 중요하다고 생각하는 이유는?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우리가 운영하는 모든 데이터 플로우는 태스크로 지정되어 실행하는 도중 실패할 수 있기 때문에 이를 인식하고 재시도하고 관리하기 위해 필요합니다 즉 Workflow를 필수적으로 모니터링 할 수 있어야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;태스크의 정기적인 스케줄 실행과 결과 통지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;태스크 간의 의존 관계 정의와 정해진 순서대로 실행하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;태스크의 실행결과를 영속적으로(필요하다면 DB로) 보관하고 오류 발생시 재실행 하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 복구를 위해 재실행할 때는 동일 태스크를 여러 번 실행해도 동일한 결과를 만들어야 하기 때문에 상황에 따라 멱등성을 이끌어 내야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테이블을 삭제한 뒤 다시 생성하는 것 처럼 태스크를 여러번 시도해도 실패하거나 이미 들어간 데이터로 인해 시스템에 문제가 없어야 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;필수적으로 대용량 데이터를 다루는 부분이기 때문에 매우 중요하고 절대적으로 동작이 365일동안 무중단으로 장애없이 유지되야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래 그림으로 RDS에서 BigQuery로 데이터 이관 구조를 표현할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3O8km/btsQ6ihBxSs/onds9ibpykj7u2ac4m7lz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3O8km/btsQ6ihBxSs/onds9ibpykj7u2ac4m7lz1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3O8km/btsQ6ihBxSs/onds9ibpykj7u2ac4m7lz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3O8km%2FbtsQ6ihBxSs%2Fonds9ibpykj7u2ac4m7lz1%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;1076&quot; height=&quot;906&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Digdag 구조&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해당 시스템의 구조나 중요도는 설명했으니 이제 본론으로 들어가서 비즈니스 적으로 협업을 하기 위한 방법에 접근해야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 시스템은 다양한 데이터를 모두 다루기 때문에 다양한 비즈니스들이 엮여 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들면 KOP, CRM, WMS, SCM, Campaign, PILS, EDW 등 연관관계 시스템들의 다양한 Database와 하위 시스템의 데이터들을 이관해야 하기 때문에 Embulk 관리자로서는 각각의 비즈니스에 대해 알지 못합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 부분을 쉽게 파악하기 위해 dig 파일이나 embulk를 실행하는 sh 파일이나 yaml 파일에 규칙을 두어 관리하고 있습니다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;프로젝트를 구성하는 디렉토리&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;.gitlab : gitlab 관련 md 문서 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;add-ons : ruby gem 에 올라가지 않은 자체 개발 customizing 된 plugin 프로젝트들&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;embulk-input-redis-restock : input data를 json 형태로 변환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;embulk-output-redis-expires : output data에 expire time 을 지정할 수 있게 함(내재화 진행 중)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;digdag-projects&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;digdag-porjects-fnckop&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;digdag 스케줄링 된 work flow 에 대해 지정하는 폴더&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;.dig 파일들이 모여 있는 곳&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;cron 스케줄 시간 지정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;parallel 동작 지정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;동작시킬 sh 파일 지정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;doc : 메뉴얼 md 문서 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;embulk-projects&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;embulk 로 실행될 shell script 들이 모여 있는 곳&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;하위에 yml.liquid 파일에 대한 명세를 보관&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;in 폴더: In 쪽 Schema 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;out 폴더: out 쪽 Schema 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;select 폴더: in 쪽 Schema 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;filter 폴더: 가공이 필요한 경우 지정하는 파일 (컬럼단위 변환, 개인정보 관련 masking 컬럼 지정)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;GCP : GCP 계정 관련 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;lib : 사용하고 있는 Datasource 관련 jar 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;etc&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;docker 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;jenkins 파일&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;digdag 디렉토리에 비즈니스 명세를 작성&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;규칙 1. Source To Target 지정&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;목적 : dig 파일만 보고 해당 작업이 어떤 작업인지 바로 파악할 수 있을 것&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;digdag 파일에서는 하나의 cron 스케줄로 관리 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각각의 파일에서는 동일한 시간대의 workflow 가 모이게 되고 같은 방향의 Source / Target 이라도 스케줄에 따라 dig 파일이 구분됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어 Bigquery To Campaign 이나 Bigquery To Redis 인 경우 시간/일간/월간 스케줄에 따라각각 구분됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하나의 태스크는 테이블 단위로 명령을 수행합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;명령이 수행되는 파일은 shell script로 작성되어 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;embulk-project 디렉토리에서 동작 파일을 작성&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;규칙 2. Source 에서 연결할 Target Data Source를 1 : N 관계가 되도록 지정&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;목적: 현재 구성된 Source 쪽과 Target 을 하나의 폴더에서 관리하고 하위 폴더에서 Target 관련 파일을 관리&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Target 디렉토리에서 계정 단위로 파일을 작성&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;규칙 3. 접속하는 Datasource 계정명으로 sh 파일과 yml 파일을 작성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;목적: embulk 관리자는 Database 관리자는 아니기 때문에 어떤 DB 구성을 사용해야 하는지 알 수없어 파일 이름만 보고 바로 알 수 있게 지정&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;digdag 파일이 어떤 스케줄과 soruce / target 을 알고자 함이라면 어떤 database 구성으로 작업하는 파일인지 또는 상황에 따라 재사용을 할 것인지 바로 알기 위한 목적으로 구성했습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 3가지 규칙으로 embulk 의 비즈니스 개발을 설명해 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;Source 와 Target database가 무엇인지 알아야 한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Source 와 Target 간 어떤 시간 스케줄로 동작할지 알아야 한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Source 와 Target 이 어떤 connection 으로 연결되는지 알아야 한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;상황에 따라 in / out / select / where / filter 작업으로 테이블 하위 컬럼 단위까지 조작이 필요한지 알아야 한다&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;i&gt;&lt;span&gt;결국 embulk 와 digdag 로 협업하려면 해당 정보들이 필요합니다&lt;/span&gt;&lt;/i&gt;&lt;/u&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위의 내용을 바탕으로 Asana ticket을 만들어 봤습니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;841&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCzKfe/dJMb9QedM6E/83BwGsEaZEwLZlfekYIWrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCzKfe/dJMb9QedM6E/83BwGsEaZEwLZlfekYIWrk/img.png&quot; data-alt=&quot;Asana Ticket&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCzKfe/dJMb9QedM6E/83BwGsEaZEwLZlfekYIWrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCzKfe%2FdJMb9QedM6E%2F83BwGsEaZEwLZlfekYIWrk%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;895&quot; height=&quot;841&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;841&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Asana Ticket&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Embulk를 운영해보니 오픈소스 기반이고 주로 비즈니스 처리 위주여서 Tool 자체에 대해 직접 코딩을 하거나 하는 부분은 없었습니다 (github에 오픈소스가 있어서 불가능한 부분은 아닙니다)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span&gt;다만 이렇게 폴더나 파일을 어떻게 관리하지?&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span&gt;해당 비즈니스가 확장되는 부분을 어떻게 표현해야 하지?&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 문제 해결을 어떻게 해야 하는지 초반 시행착오가 있던 것 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;생각해보니 데이터를 관리하는 프로그래머의 역할과 파일을 관리해야 하는 부분이 별반 차이가 없지 않나? 하는 생각이 드는 것 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로그램의 메모리에 접근해서 New로 동적할당을 하거나 List, Set, Map, Dictinary 와 같은 자료구를 만들어서 효율적인 데이터를 뽑아내는 것과 수십 수백개의 연관 파일들을 어떻게 명시하고 관리하는 부분도 프로그래머가 더 연구해야 하는 부분이라는 생각이 들었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;J&lt;/span&gt;&lt;b&gt;&lt;i&gt;&lt;span&gt;ava, Kotlin, C#, C++ 같은 언어를 쓰는 명령형 기반이 아닌 yaml, css, xml 같은 선언형 기반으로 코딩한 경험이 되었다고 보고 있습니다&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 부분에 있어 더 좋은 방법이 어떤 것이 있고 이런 ETL 또는 데이터 엔지니어링과 관련된 부분은 어떻게 더 잘할 수 있을지 고민해 볼 수 있는 부분이 되어 좋았습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;References&lt;/span&gt;&lt;/span&gt;&lt;/h2&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://www.embulk.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Embulk&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digdag.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Digdag&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bcho.tistory.com/1126&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;빅데이터 수집을 위한 데이터 수집 솔루션 Embulk 소개&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itpassion.tistory.com/entry/ETL%EC%9D%B4%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;ETL 이란?&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@hyuil/DIGDAG&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Digdag 소개&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pearlluck.tistory.com/579&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;빅데이터의 파이프라인&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/shiozaki/items/f79eecf8e1878aa64a40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Digdag와 Embulk를 사용해서 데이터 동기화&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://getchan.github.io/data/airflow_1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Airflow를 이용한 배치 데이터 처리(1)&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/120</guid>
      <comments>https://hankkuu.tistory.com/120#entry120comment</comments>
      <pubDate>Sun, 19 Oct 2025 22:28:08 +0900</pubDate>
    </item>
    <item>
      <title>밑바닥부터 만드는 컴퓨팅 시스템을 읽고(컴파일러 만들어보쟈!)</title>
      <link>https://hankkuu.tistory.com/119</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0039R/dJMb9dAp4r3/nISWlckvhBKd9EB1nulGCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0039R/dJMb9dAp4r3/nISWlckvhBKd9EB1nulGCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0039R/dJMb9dAp4r3/nISWlckvhBKd9EB1nulGCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0039R%2FdJMb9dAp4r3%2FnISWlckvhBKd9EB1nulGCK%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;592&quot; height=&quot;762&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Why &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 당신이 &lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;독학 엔지니어&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나 부트캠프 졸업생이라면 컴퓨터 과학을 배우는 것도 놓쳐서는 안됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;세상에는 두 부류의 소프트웨어 엔지니어가 있습니다. 첫 번째 부류는 컴퓨터 과학을 깊이 이해하고 도전적이고 혁신적인 일을 수행하는 엔지니어들입니다. 이들은 시간이 지나면서 중요한 상업 프로젝트나 오픈소스 프로젝트에서 성취감을 느끼고, 기술적 리더십을 쌓으며, 더 높은 보수를 받습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 부류는 특정 툴이나 기술에만 집중하며, 기초를 깊이 이해하지 못한 채 표면적으로만 기술을 익힙니다. 시간이 지나면서 이들에 대한 고용 기회는 감소하고, 중요한 일에서 배제될 가능성이 큽니다. 고용 안정을 원하거나 첫 번째 부류의 엔지니어가 되고 싶다면, 컴퓨터 과학을 깊이 배우는 것이 중요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #0097a7;&quot;&gt;&lt;a href=&quot;https://github.com/minnsane/TeachYourselfCS-KR&quot;&gt;Teach Yourself CS&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 내용 발췌&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런데 이 말이 진짜일까?&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;위 책내용에 대해서 본격적으로 들어가기 전에 간단히 Typescript 에서 소개해주는 내용도 봤습니다 (이것도 스터디로 진행)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RT12t/dJMb9MJD5jb/aJI2sZlFMYHk73naEIq4DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RT12t/dJMb9MJD5jb/aJI2sZlFMYHk73naEIq4DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RT12t/dJMb9MJD5jb/aJI2sZlFMYHk73naEIq4DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRT12t%2FdJMb9MJD5jb%2FaJI2sZlFMYHk73naEIq4DK%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;195&quot; height=&quot;250&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;518&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;프로그래밍 언어&lt;/li&gt;
&lt;li&gt;새로운 타입스크립트 고유 구문이 포함 됨&lt;/li&gt;
&lt;li&gt;타입 검사기&lt;/li&gt;
&lt;li&gt;파일에서 생성된 변수 함수를 이해하고 잘못된 부분을 알려 주는 &lt;span style=&quot;color: #ff0000;&quot;&gt;프로그램&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;컴파일러&lt;/li&gt;
&lt;li&gt;자바스크립트 코드를 생성 해주는 &lt;span style=&quot;color: #ff0000;&quot;&gt;프로그램&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;언어 서비스VS code 와 같은 편집기에 유용한 유틸리티 제공법을 알려주는 &lt;span style=&quot;color: #ff0000;&quot;&gt;프로그램&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;3개의 프로그램&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;과 타입스크립트 고유 구문으로 이루어져 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqMrV7/dJMb9N9Dfsl/Gz7WRSmI4xVYjOFTkTDyiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqMrV7/dJMb9N9Dfsl/Gz7WRSmI4xVYjOFTkTDyiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqMrV7/dJMb9N9Dfsl/Gz7WRSmI4xVYjOFTkTDyiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqMrV7%2FdJMb9N9Dfsl%2FGz7WRSmI4xVYjOFTkTDyiK%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;755&quot; height=&quot;302&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;388&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;770&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/90Zpt/dJMb9NIyF3n/5JhxM9AOOHeaczlQEdj6N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/90Zpt/dJMb9NIyF3n/5JhxM9AOOHeaczlQEdj6N0/img.png&quot; data-alt=&quot;https://www.huy.rocks/everyday/04-01-2022-typescript-how-the-compiler-compiles&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/90Zpt/dJMb9NIyF3n/5JhxM9AOOHeaczlQEdj6N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F90Zpt%2FdJMb9NIyF3n%2F5JhxM9AOOHeaczlQEdj6N0%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;770&quot; height=&quot;762&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.huy.rocks/everyday/04-01-2022-typescript-how-the-compiler-compiles&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타입스크립트 컴파일러가 동작하는 방식, 즉 tsc 명령어를 눌렀을 때 일어나는 작업은 크게 아래와 같이 나눠볼 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;tsconfig 읽기: 타입스크립트 프로젝트라면, root에 tsconifg.json을 읽는 작업부터 시작할 것이다.&lt;/li&gt;
&lt;li&gt;preprocess: 파일의 root 부터 시작해서 imports로 연결된 가능한 모든 파일을 찾는다.&lt;/li&gt;
&lt;li&gt;tokenize &amp;amp; parse: .ts로 작성된 파일을 신택스 트리로 변경한다.&lt;/li&gt;
&lt;li&gt;binder: 3번에서 변경한 신택스 트리를 기준으로, 해당 트리에 있는 symbol (const 등) 을 identifier로 변경한다.&lt;/li&gt;
&lt;li&gt;타입체크: binder와 신택스 트리를 기준으로 타입을 체크한다.&lt;/li&gt;
&lt;li&gt;transform: 신택스트리를 1번에서 읽었던 옵션에 맞게 변경한다.&lt;/li&gt;
&lt;li&gt;emit: 신택스 트리를 .js .d.ts파일 등으로 변경한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3번까지의 과정이 소스코드를 읽어 데이터로 만드는 과정&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4, 5가 타입체킹 과정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6, 7 을 파일을 만드는 과정&lt;/span&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;646&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rycIM/dJMb9Mv7iJu/nnnKp8HA57lRSBPUqN9keK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rycIM/dJMb9Mv7iJu/nnnKp8HA57lRSBPUqN9keK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rycIM/dJMb9Mv7iJu/nnnKp8HA57lRSBPUqN9keK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrycIM%2FdJMb9Mv7iJu%2FnnnKp8HA57lRSBPUqN9keK%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;646&quot; height=&quot;658&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 디테일한 내용을 알려고 하면 자세하게 &lt;a href=&quot;https://yceffort.kr/2022/05/how-typescript-compiler-works&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정리한 내용&lt;/a&gt;을 참고해 봐도 좋습니다&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHdPSq/dJMb9XxzadC/ZD10JkpOb2iTlkVSASz6fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHdPSq/dJMb9XxzadC/ZD10JkpOb2iTlkVSASz6fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHdPSq/dJMb9XxzadC/ZD10JkpOb2iTlkVSASz6fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHdPSq%2FdJMb9XxzadC%2FZD10JkpOb2iTlkVSASz6fk%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;502&quot; height=&quot;442&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;tsc 명령어로 ts 파일을 js 파일로 바꿉니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;얘기하고 싶은 부분은 jdk 를 설치하면 javac로 .java 파일을 해석하듯 typescript를 설치하면 tsc 로 .ts 파일을 해석할 수 있었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;다른 컴파일러를 구현한다면? &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래와 비슷하겠네요 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;loadModuleFromFile(.. , .. , .. , .. )&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zDqJG/dJMb9Xxzaec/P6ROYw1VC4Kk8jzmuwXNx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zDqJG/dJMb9Xxzaec/P6ROYw1VC4Kk8jzmuwXNx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zDqJG/dJMb9Xxzaec/P6ROYw1VC4Kk8jzmuwXNx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzDqJG%2FdJMb9Xxzaec%2FP6ROYw1VC4Kk8jzmuwXNx1%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;1182&quot; height=&quot;344&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;밑 바닥부터 만드는 컴퓨팅 시스템의 목차는 아래와 같습니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;불 논리&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;2.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;불 연산(중요)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;순차 논리&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기계어&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컴퓨터 아키텍처&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;6.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;어셈블러(기초 적인 번역)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;7.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;가상머신 1: 스택산술 (중간급 번역기)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;8.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;가상머신 2: 프로그램 제어&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;9.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;고수준 언어&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ff00ff;&quot;&gt;10.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ff00ff;&quot;&gt;컴파일러 1: 구문분석 (아주 복잡한 번역기)&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ff00ff;&quot;&gt;11.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ff00ff;&quot;&gt;컴파일러 2: 코드생성&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;12.&lt;/span&gt;&lt;/span&gt;&lt;s&gt;&lt;span style=&quot;color: #000000;&quot;&gt;운영체제 (해당 항목은 따로 깊게)&lt;/span&gt;&lt;/s&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;s&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/s&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXLkJv/dJMb9PzB6cG/1jcEwz3HCkUiZC4mGNCE50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXLkJv/dJMb9PzB6cG/1jcEwz3HCkUiZC4mGNCE50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXLkJv/dJMb9PzB6cG/1jcEwz3HCkUiZC4mGNCE50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXLkJv%2FdJMb9PzB6cG%2F1jcEwz3HCkUiZC4mGNCE50%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;1450&quot; height=&quot;790&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이걸 어떻게 이해할 수 있을까?&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저는 Chip 그림의 명세만 보고는 도저히 내부를 상상할 수가 없었습니다 (어떠한 가설조차 불가능)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 관련 내용을 설명해주는 영상을 보고 역으로 답지를 먼저 보고 문제를 마지막에 보았습니다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 그림을 직접 손으로 따라 그려봤습니다&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGLDlp/dJMb9hW7BRE/kFmeziWMytvNuCLbe1uwyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGLDlp/dJMb9hW7BRE/kFmeziWMytvNuCLbe1uwyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGLDlp/dJMb9hW7BRE/kFmeziWMytvNuCLbe1uwyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGLDlp%2FdJMb9hW7BRE%2FkFmeziWMytvNuCLbe1uwyK%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;1236&quot; height=&quot;560&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #0097a7;&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=dGKIZAHTxVk&quot;&gt;영상 1&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #0097a7;&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=2F1_-OOFLhk&quot;&gt;영상&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;u&gt;&lt;span style=&quot;color: #0097a7;&quot;&gt; &lt;/span&gt;&lt;/u&gt;&lt;u&gt;&lt;span style=&quot;color: #0097a7;&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=2F1_-OOFLhk&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;직접 한번 그려 보쟈!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NgS2e/dJMb9Qyv84a/zeAqleAn9SFI70TFMYANHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NgS2e/dJMb9Qyv84a/zeAqleAn9SFI70TFMYANHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NgS2e/dJMb9Qyv84a/zeAqleAn9SFI70TFMYANHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNgS2e%2FdJMb9Qyv84a%2FzeAqleAn9SFI70TFMYANHk%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;850&quot; height=&quot;644&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아주 간단히 얘기하면 아래를 하기 위해서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;X + Y = ? &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Bit 반전이 되는 6개의 Input 값이 있고&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1개의 중요한 Output 값과 2개의 부가적인 OutPut 값이 나오게 되는 것&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(책 속의 ALU 에서는.. 이게 표준? )&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ALU 내부 방식에 대한 설계는 어떻게 가능할 수 있는가? 이걸로 스터디 원들과 메신저로 얘기도 해봤습니다 (너무 막연하더군요)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCBkKB/dJMb8Vs0eHd/Yr7k2aATE2ZcxRcZAg4ZNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCBkKB/dJMb8Vs0eHd/Yr7k2aATE2ZcxRcZAg4ZNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCBkKB/dJMb8Vs0eHd/Yr7k2aATE2ZcxRcZAg4ZNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCBkKB%2FdJMb8Vs0eHd%2FYr7k2aATE2ZcxRcZAg4ZNk%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;1246&quot; height=&quot;706&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 HW 이야기 입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저만의 결론은 단지 사칙연산이라기보다는 그렇다고 논리회로라기 보다는 그저 겉 핧기식 판단이라도 해보자면 입력과 출력이었습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 아직 이야기(코드)의 흐름은 연결이 되지 않았지만 철저하게 기계화된 약속과 그 약속을 가지고 Chip 이라는 것이 만들어졌습니다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Chip 은 단순하지만 Chip 을 활용하면 메모리나 CPU 를 조합할 수 있습니다 객체지향적 컨셉이라기보다는 천재의 영역같은 조합이 이루어지면 저희는 이걸 컴퓨터 아키텍처라고 부른다는 생각이 드는군요&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eBX22j/dJMb9fLLQkB/i8EKDfPMj1Igc1Q0sIVQnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eBX22j/dJMb9fLLQkB/i8EKDfPMj1Igc1Q0sIVQnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eBX22j/dJMb9fLLQkB/i8EKDfPMj1Igc1Q0sIVQnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeBX22j%2FdJMb9fLLQkB%2Fi8EKDfPMj1Igc1Q0sIVQnk%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;1450&quot; height=&quot;814&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;814&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgZvxd/dJMb9YpHUry/t9bBoNI6OUmNOA9T74V0hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgZvxd/dJMb9YpHUry/t9bBoNI6OUmNOA9T74V0hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgZvxd/dJMb9YpHUry/t9bBoNI6OUmNOA9T74V0hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgZvxd%2FdJMb9YpHUry%2Ft9bBoNI6OUmNOA9T74V0hK%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;1358&quot; height=&quot;634&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;634&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;i 와 sum 두 개의 변수와 loop, end 두개의 label&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;번역된 코드는 주소 0부터 시작하는 메모리에 저장하고 변수들은 1024부터 할당&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;다음으로 소스코드에 새로운 기호가 나타날 때마다 테이블에 추가하는 방식으로 기호테이블에 추가하는 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;다 만들어지면 이 테이블을 이용하게 됩니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oRa3K/dJMb9LjF20Z/VUdSsnakE4WxxNhpBtf2Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oRa3K/dJMb9LjF20Z/VUdSsnakE4WxxNhpBtf2Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oRa3K/dJMb9LjF20Z/VUdSsnakE4WxxNhpBtf2Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoRa3K%2FdJMb9LjF20Z%2FVUdSsnakE4WxxNhpBtf2Uk%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;1428&quot; height=&quot;552&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기계어 프로그램은 명령어를 코드화 한 것&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2진 코드는 연상기호를 사용하게 되어 있어서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1010 0001 0001 0001(16bit) 으로 된 코드는 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1010 &amp;rarr; ADD&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0010 &amp;rarr; R2&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0001 &amp;rarr; R1&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0001 &amp;rarr; R3&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 기호를 좀 더 추상화하면 2진 명령어 대신에&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기호를 텍스트로 입력해서 프로그램을 작성가능 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 기호 표기법을 어셈블리 언어, 어셈블러라고 합니다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEQ75M/dJMb9LjF22G/C5vRSqhhNWYF0JMvFXgcPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEQ75M/dJMb9LjF22G/C5vRSqhhNWYF0JMvFXgcPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEQ75M/dJMb9LjF22G/C5vRSqhhNWYF0JMvFXgcPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEQ75M%2FdJMb9LjF22G%2FC5vRSqhhNWYF0JMvFXgcPK%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;1404&quot; height=&quot;724&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;724&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;1440&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DEYen/dJMb9LDYqiJ/FkxSYcb904VnvcZ4APT4y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DEYen/dJMb9LDYqiJ/FkxSYcb904VnvcZ4APT4y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DEYen/dJMb9LDYqiJ/FkxSYcb904VnvcZ4APT4y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDEYen%2FdJMb9LDYqiJ%2FFkxSYcb904VnvcZ4APT4y0%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;1440&quot; height=&quot;716&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;716&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;1380&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Ltsu/dJMb9ap9X28/v1Bbbx6kKkMSx55eCA22c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Ltsu/dJMb9ap9X28/v1Bbbx6kKkMSx55eCA22c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Ltsu/dJMb9ap9X28/v1Bbbx6kKkMSx55eCA22c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Ltsu%2FdJMb9ap9X28%2Fv1Bbbx6kKkMSx55eCA22c1%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;1380&quot; height=&quot;790&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;790&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;1426&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvFtCt/dJMb9fLLQsm/E8O6fvbiyKEzNOXU02WDPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvFtCt/dJMb9fLLQsm/E8O6fvbiyKEzNOXU02WDPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvFtCt/dJMb9fLLQsm/E8O6fvbiyKEzNOXU02WDPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvFtCt%2FdJMb9fLLQsm%2FE8O6fvbiyKEzNOXU02WDPK%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;1426&quot; height=&quot;786&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFk3xR/dJMb9Nu1nzC/NVbFWrj6FdHtkezKNNRuu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFk3xR/dJMb9Nu1nzC/NVbFWrj6FdHtkezKNNRuu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFk3xR/dJMb9Nu1nzC/NVbFWrj6FdHtkezKNNRuu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFk3xR%2FdJMb9Nu1nzC%2FNVbFWrj6FdHtkezKNNRuu0%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;1344&quot; height=&quot;152&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;152&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;1358&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn8AYu/dJMb9bWUbki/n4aIyPkRxDSIkmhPP87uV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn8AYu/dJMb9bWUbki/n4aIyPkRxDSIkmhPP87uV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn8AYu/dJMb9bWUbki/n4aIyPkRxDSIkmhPP87uV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn8AYu%2FdJMb9bWUbki%2Fn4aIyPkRxDSIkmhPP87uV0%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;1358&quot; height=&quot;108&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스터디원의 Question?&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;왜 VM 이라는 중간자를 거쳐서 해석(컴파일)같은 과정을 거쳐야 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBy5Cn/dJMb8WrUr8c/wVxKst90R6MCm4OHpf8i00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBy5Cn/dJMb8WrUr8c/wVxKst90R6MCm4OHpf8i00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBy5Cn/dJMb8WrUr8c/wVxKst90R6MCm4OHpf8i00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBy5Cn%2FdJMb8WrUr8c%2FwVxKst90R6MCm4OHpf8i00%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;741&quot; height=&quot;1144&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;812&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;1420&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MPGXt/dJMb88lzEDZ/G1Etj8yrg04nkMKskC6K40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MPGXt/dJMb88lzEDZ/G1Etj8yrg04nkMKskC6K40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MPGXt/dJMb88lzEDZ/G1Etj8yrg04nkMKskC6K40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMPGXt%2FdJMb88lzEDZ%2FG1Etj8yrg04nkMKskC6K40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;495&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;818&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 중요한 기반은 스택산술머신을 구현하는 것&lt;/span&gt; &lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lQKl6/dJMb9ap9X7K/IWipMideZgCAyCMFkunDak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lQKl6/dJMb9ap9X7K/IWipMideZgCAyCMFkunDak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lQKl6/dJMb9ap9X7K/IWipMideZgCAyCMFkunDak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlQKl6%2FdJMb9ap9X7K%2FIWipMideZgCAyCMFkunDak%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;654&quot; height=&quot;710&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;명세 &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;산술 명령: 스택에서 산술 및 논리 연산을 수행함&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;메모리 접근 명령: 스택과 가상 메모리 세그먼트 사이에 데이터를 주고 받는 명령 &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로그램 흐름 명령: 조건 및 무조건 분기 연산을 가능하게 함&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수 호출 명령: 함수를 호출하고 결과를 반환 함&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXAh0N/dJMb9Wel9Wn/7HdjDLTxECnMgW3g42DnT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXAh0N/dJMb9Wel9Wn/7HdjDLTxECnMgW3g42DnT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXAh0N/dJMb9Wel9Wn/7HdjDLTxECnMgW3g42DnT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXAh0N%2FdJMb9Wel9Wn%2F7HdjDLTxECnMgW3g42DnT0%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;924&quot; height=&quot;474&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;명세에 따라 아래와 같은 어셈블러 코드를 이해하는 부분을 구현할 수 있습니다&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZCMsq/dJMb86adThd/8wZASTvY0F0tmmxHs7uBwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZCMsq/dJMb86adThd/8wZASTvY0F0tmmxHs7uBwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZCMsq/dJMb86adThd/8wZASTvY0F0tmmxHs7uBwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZCMsq%2FdJMb86adThd%2F8wZASTvY0F0tmmxHs7uBwk%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;429&quot; height=&quot;934&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;710&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;950&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9uAU1/dJMb84cpfu5/W8SKCIU5HokW1R2tkDy1c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9uAU1/dJMb84cpfu5/W8SKCIU5HokW1R2tkDy1c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9uAU1/dJMb84cpfu5/W8SKCIU5HokW1R2tkDy1c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9uAU1%2FdJMb84cpfu5%2FW8SKCIU5HokW1R2tkDy1c1%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;950&quot; height=&quot;460&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;명세에 따라 스택에 연산을 어떻게 할지 분기 작성을 구현할 수 있습니다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wkmlt/dJMb8Z90vGV/iOuPjVaUNYrDlMDt06KKy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wkmlt/dJMb8Z90vGV/iOuPjVaUNYrDlMDt06KKy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wkmlt/dJMb8Z90vGV/iOuPjVaUNYrDlMDt06KKy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWkmlt%2FdJMb8Z90vGV%2FiOuPjVaUNYrDlMDt06KKy1%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;675&quot; height=&quot;909&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;722&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;1430&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bycs1/dJMb8Z90vG9/heNeIMTxJmZvej2kTD1ezK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bycs1/dJMb8Z90vG9/heNeIMTxJmZvej2kTD1ezK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bycs1/dJMb8Z90vG9/heNeIMTxJmZvej2kTD1ezK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBycs1%2FdJMb8Z90vG9%2FheNeIMTxJmZvej2kTD1ezK%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;1430&quot; height=&quot;416&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메인함수에서는 스택과 서브루틴을 사용해서 넣고 빼고 하면서 흐름을 진행합니다&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이것이 가능한 이유는 스택기반 산술머신이 구현이 되어 있기 때문입니다 즉, 메모리에 넣고 뺴고 하면서 논리를 해석할 수 있는 구조를 만들었습니다&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPxTKW/dJMb8Z90vHa/IWkznFk05kCjYzwpJN5p4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPxTKW/dJMb8Z90vHa/IWkznFk05kCjYzwpJN5p4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPxTKW/dJMb8Z90vHa/IWkznFk05kCjYzwpJN5p4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPxTKW%2FdJMb8Z90vHa%2FIWkznFk05kCjYzwpJN5p4k%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;1432&quot; height=&quot;666&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;666&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;1026&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XZb2t/dJMb8Y4kE5J/xGz2jwK7UDWvZrkJL4asyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XZb2t/dJMb8Y4kE5J/xGz2jwK7UDWvZrkJL4asyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XZb2t/dJMb8Y4kE5J/xGz2jwK7UDWvZrkJL4asyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXZb2t%2FdJMb8Y4kE5J%2FxGz2jwK7UDWvZrkJL4asyK%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;1026&quot; height=&quot;630&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;630&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;1402&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PUHbx/dJMb85bjGtV/1L6ktEVKkk0oAPiMSxrAo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PUHbx/dJMb85bjGtV/1L6ktEVKkk0oAPiMSxrAo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PUHbx/dJMb85bjGtV/1L6ktEVKkk0oAPiMSxrAo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPUHbx%2FdJMb85bjGtV%2F1L6ktEVKkk0oAPiMSxrAo1%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;1402&quot; height=&quot;786&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;786&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;1404&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDVFER/dJMb9PsQfrX/aeka1HgeH6tRksby3VzLeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDVFER/dJMb9PsQfrX/aeka1HgeH6tRksby3VzLeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDVFER/dJMb9PsQfrX/aeka1HgeH6tRksby3VzLeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDVFER%2FdJMb9PsQfrX%2Faeka1HgeH6tRksby3VzLeK%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;1404&quot; height=&quot;806&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;806&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;1344&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vB7Dz/dJMb9PsQfr5/xp5KLtT1pArQzdPrR25bS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vB7Dz/dJMb9PsQfr5/xp5KLtT1pArQzdPrR25bS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vB7Dz/dJMb9PsQfr5/xp5KLtT1pArQzdPrR25bS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvB7Dz%2FdJMb9PsQfr5%2Fxp5KLtT1pArQzdPrR25bS0%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;1344&quot; height=&quot;152&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;왜 이런 세부사항을 다 살펴봐야 하는지 의아해하는 사람들에게&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRW7yw/dJMb9O1L1cx/lvmWYPaDNokNiMg1ktlI8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRW7yw/dJMb9O1L1cx/lvmWYPaDNokNiMg1ktlI8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRW7yw/dJMb9O1L1cx/lvmWYPaDNokNiMg1ktlI8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRW7yw%2FdJMb9O1L1cx%2FlvmWYPaDNokNiMg1ktlI8K%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;586&quot; height=&quot;584&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;이 책에서 가장 좋았던 부분 &lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;더 나은 고수준 프로그래머가 될 수 있는 &lt;/span&gt;&lt;span style=&quot;color: #ff00ff;&quot;&gt;희망&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;을 준 책&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 복잡하고 어려운 문제를 만난다면(워딩을 재해석)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재귀(반복적인 부분 &amp;rarr; 아키텍처로 누구나 따라할 수 있는 공식화가 가능할 수 있는 부분)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;피호출자를 위한 인수, 함수프레임, 지역변수, 작업스택으로 구성된 메모리 블록을 스택에 추가 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈을 분리하고 관심사로 나누어 복잡도를 단순화 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;효율성을 고려해서 재귀코드를 순차 코드로 다시 표현하려 한다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정규화/반정규화 혹은 성능이나 비즈니스 요구사항을 고려해서 최적의 기능과 성능을 도출&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E5jpW/dJMb82TclcY/7jI4rHy2VylEqxCEPCusm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E5jpW/dJMb82TclcY/7jI4rHy2VylEqxCEPCusm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E5jpW/dJMb82TclcY/7jI4rHy2VylEqxCEPCusm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE5jpW%2FdJMb82TclcY%2F7jI4rHy2VylEqxCEPCusm0%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;1344&quot; height=&quot;170&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;고수준 프로그래머가 가당키나 한 말인가&amp;hellip; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;100번 1000번 다시 태어나도 그렇게 될 확률이나 자신이 없음&amp;hellip; ㅎㅎㅎ&amp;hellip; ㅜㅠㅡㅠㅜ&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇다면 보편적으로 고수준(좋은) 프로그래머는 어떤 역량을 갖춘걸까? &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단지 실력이라는 부분으로 모든 것을 치환하기는 힘들것으로 생각됩니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ALU나 어셈블러, VM번역기 그리고 컴파일러 또는 운영체제 마저도 복잡하고 어려운 내부 시스템을 갖추고 있는 것으로 보입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그럼에도 불구하고 매우 높은 안정성과 성능을 보여주고 있는데 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공통적으로 모듈이 되어 있고 관심사 별로 처리 방법이 잘 포장되어 있는 것으로 보입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그러면 이런 규칙들을 잘 설계하고 명세하는 것이 일단 시작이지 않을까? &lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 모든 구현에 대한 인터페이스 설계가 정말 중요한 것이지 않을까?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c66EC2/dJMb9OgoDsq/eULc79hwUWOqgR6b30eXB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c66EC2/dJMb9OgoDsq/eULc79hwUWOqgR6b30eXB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c66EC2/dJMb9OgoDsq/eULc79hwUWOqgR6b30eXB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc66EC2%2FdJMb9OgoDsq%2FeULc79hwUWOqgR6b30eXB0%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;728&quot; height=&quot;762&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;틈틈히 비즈니스에서 기술을 사용하는 부분에 대해서 인터페이스에 대해 문서화를 해보고 있습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;과거에는 팀장이나 선임이 만든 인터페이스 문서로만 봤던 부분을 직접 작성해보니 작은 부분도 고민이되고 어려운 부분이 많은 것 같습니다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특히 혼자 정하기 어려운 부분도 많고 꾸준히 생각해봐야 하는 부분들도 있습니다 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 기능은 추가되어야 하고 코드는 늘어나야 하며 일정은 준수해야 합니다&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 생산성이 뛰어나서 시간이 더 생겨서 문서를 쓸 수 있는 것이 아니라 어느정도 본인 희생적인 부분도 필요해 보입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 내부구조를 설계하고 왜 이렇게 해야 하는지 공감이되고 품질과 성능과 그리고 생산성 좋은 인터페이스도 고려해야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZNI8t/dJMb9XK6ly6/1EbQDWHhGpfYKZFiouoj9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZNI8t/dJMb9XK6ly6/1EbQDWHhGpfYKZFiouoj9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZNI8t/dJMb9XK6ly6/1EbQDWHhGpfYKZFiouoj9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZNI8t%2FdJMb9XK6ly6%2F1EbQDWHhGpfYKZFiouoj9k%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;1358&quot; height=&quot;790&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;790&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;1446&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JiBSZ/dJMb9XK6lzh/8PxLq61JNNdZQ6nKIRMSvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JiBSZ/dJMb9XK6lzh/8PxLq61JNNdZQ6nKIRMSvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JiBSZ/dJMb9XK6lzh/8PxLq61JNNdZQ6nKIRMSvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJiBSZ%2FdJMb9XK6lzh%2F8PxLq61JNNdZQ6nKIRMSvK%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;1446&quot; height=&quot;818&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;818&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;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfVwAx/dJMb9bo4hoT/zvqSocxMNy3SNG96rtOir0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfVwAx/dJMb9bo4hoT/zvqSocxMNy3SNG96rtOir0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfVwAx/dJMb9bo4hoT/zvqSocxMNy3SNG96rtOir0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfVwAx%2FdJMb9bo4hoT%2FzvqSocxMNy3SNG96rtOir0%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;906&quot; height=&quot;78&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;절대적으로 시간에 쫓기면 더 많은 상상을 하기가 어려운 것 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;시간에 쫓기더라도 시간을 고민하기 전에 단 한번이라도 한번 더 좋은 방법에 대해 More Thinking 을 항상 해보는 것은 어떨지.. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;독학 엔지니어라는 워딩이 있는지 몰랐는데 ㅎㅎ 어느정도 지식이 쌓이기 전까지는 계속 수많은 상상과 가설로 문제에 접근한 적도 많았습니다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이처럼 Thinking 은 비용이 아니라고 생각되는 부분이라서 꾸준히 공부하고 검증해야 한다고 생각되네요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 스터디를 통해서 그동안 너무나 막연했던 컴퓨터 세상에 대해 조금 접근해 볼 수 있었고 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무엇보다 아무런 준비나 부담없이 그냥 모여서 자유롭게 토론하고 얘기하고 상상하고 그런 부분이 좋았던 것 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무말로 막 시작하지면 그래도 항상 결론은 책과 비슷하게 도출했던 부분이 가장 좋은 기억으로 남습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스터디를 정리하면서 과거에 제가 까막눈일때 코드를 알기 위해 모든 디버깅 포인트를 찍고 무작정 시작부터 한땀한땀 따라갔던 기억이 납니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;똑똑한 컴파일러의 도움이 없었다면 아마 저는 지금 여기에 없을지도 모르겠네요&lt;/span&gt;&lt;/u&gt;&lt;u&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;/u&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;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y3cQf/dJMb9hW7DhJ/Qu2tme713VTWqjiflTmxb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y3cQf/dJMb9hW7DhJ/Qu2tme713VTWqjiflTmxb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y3cQf/dJMb9hW7DhJ/Qu2tme713VTWqjiflTmxb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy3cQf%2FdJMb9hW7DhJ%2FQu2tme713VTWqjiflTmxb1%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;647&quot; height=&quot;1073&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;마지막으로 코틀린으로 컴퓨터언어(책에서는 핵언어)를 해석하는 파서 부터 구현을 해보려고 도전했던 부분으로 마무리합니다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;컴퓨터는 항상 어려우니 언제나 more thinking 을 하다보면 좀 더 고수준 개발자에 가까워지지 않을까 미래를 예측해 봅니다&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>My Reading/IT</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/119</guid>
      <comments>https://hankkuu.tistory.com/119#entry119comment</comments>
      <pubDate>Sun, 19 Oct 2025 02:58:46 +0900</pubDate>
    </item>
    <item>
      <title>고유 식별자 생성 방법에 대한 여러가지</title>
      <link>https://hankkuu.tistory.com/114</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;웹 어플리케이션을 설계하다 보면 많은 Entity, Model, Table 들을 마주하게 되고 정의하게 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;DB의 Table에서는 Auto Increment 기능이 있어서 개발자가 신경을 쓰지 않아도 자동 생성되는 식별자가 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 녀석은 index 도 만들어지게 되서 검색 해서 사용하는데 문제가 없지만 DB 라는 미들웨어에 종속된 id로 DB 라는 구간을 거쳐야지만 이 값을 쓸 수 있다는 제약이 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;또 숫자형의 식별자를 쓰기 때문에 문자형의 식별이나 인조키가 아닌 자연키로 쓰는 경우에도 제약이 생깁니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하나의 데이터베이스에 모든 식별자 생성이 몰리게 되면 &lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%8B%A8%EC%9D%BC_%EC%9E%A5%EC%95%A0%EC%A0%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;SPOF&lt;/span&gt;&lt;/a&gt;&lt;span&gt;(single point of failure) 라는 단일 장애지점이 되어 DB가 멈춤에 따라 식별자 생성도 불가능해 집니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 식별자 생성을 데이터 생성에 의존하기 보다는 제가 해야 하는 행위(적립/사용/취소/소멸/예약 등) 에 쓰고자 하는 needs 가 있었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;CRM의 포인트 시스템에서는 이를 하고자 DB의 식별자와는 별개로 Point Id 를 생성하게 했는데 ksuid 라는 이름의 녀석을 사용했습니다 (&lt;/span&gt;&lt;a href=&quot;https://www.twilio.com/en-us/blog/developers/a-brief-history-of-the-uuid&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;2017년 6월 8일 출시&lt;/span&gt;&lt;/a&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;KSUID는 K-Sortable Unique IDentifier의 약어입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;생성시의 타임스탬프로 &quot;자연스럽게&quot; 정렬되도록 만들어지기로 한 것으로 &lt;/span&gt;&lt;b&gt;&lt;span&gt;UNIX&lt;/span&gt;&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;b&gt;&lt;span&gt;sort &lt;/span&gt;&lt;/b&gt;&lt;span&gt;명령을 통해 일련의 KSUID를 실행하면 생성 시간별로 정렬됩다고 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;개발 당시에는 정렬이 되는 uuid 라는 요구 조건에 바로 부합되었기 때문에 큰 고민없이 도입하게 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;전체 E-Kolon 가입자 대상으로 하는 OLO 마일리지 출시를 위해 성능과 부하테스트를 하는 중 동시에 100건의 같은 Request가 왔을때 유일하게 한번만 보장하는 부분을 테스트 할 때 기존에 임직원 50%할인 포인트를 위한 기존의 2개의 포인트 타입과 새로운 타입인 OLO 마일리지간 3개의 포인트간 경합이 생겼습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;요청하는 비즈니스로만 구분하는 경우는 각각의 클라이언트를 식별할 수 있는 식별자가 없이 막히자 말아야 하는 경우까지 막아 버릴 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사실 이 경우는 clientId + pointType + 주문수량 .. 등등 여러 조합을 통해 문제를 해결할 수 있지만 제가 하고자 하는 것은 지금 문제 해결이 아닌 앞으로 다른 서비스를 만들거나 현재부터 1년 뒤 10년 뒤도 문제가 안되는 해결책이 없을까에 대한 고민이 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위의 ksuid 를 사용하면되지 않나? 라고 생각될 수 있지만 모든 비즈니스에 식별자를 만들지는 않아 식별자 생성 없이 유일성을 보장해야 하는 함수 들의 처리도 있었습니다 (DB에는 로직이 없고 거의 대부분이 kotlin 만들어진 로직이었습니다 - 레거시 쪽은 DB에 의존하는 SP, Query 가 많은 방식(이게 무조건 적인 단점은 아니고 일단 성능보다 코드레벨에서의 복잡도 개선이 우선 순위였고 추후 테스트를 거쳐 성능을 위해 디비에 로직이 포함됩니다)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;전에 &lt;/span&gt;&lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=276041776&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;가상 면접 사례로 배우는 대규모 시스템 설계 기초&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 라는 책을 본 적이 있어 관련 내용을 봤었고 7장 분산 시스템을 위한 유일 ID 생성기 설계라는 파트가 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기에 지난번 &lt;/span&gt;&lt;a href=&quot;https://hankkuu.tistory.com/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;동시성 이슈 빠르게 처리하기&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 말미에 BillKey 에 대한 언급을 했었는데 이 책에서는 비슷하게 티켓 서버(Ticket Server) 라는 예시로 설명이 되고 있습니다 하지만 이 또한 &lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%8B%A8%EC%9D%BC_%EC%9E%A5%EC%95%A0%EC%A0%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;SPOF&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 가 될 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 중요한건 장애에 취약하지 않으면서 유일한 고유 식별자 생성이 되고 다변량으로 존재하는 클라이언 들이 비즈니스간 경합이 되지 않는 방식이 필요합니다 &lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;(DB에 저장하지 않아도 비즈니스의 유일성을 보장해주는 Id 이면 됩니다)&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이는 &lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;가장 먼저 선행 하는 로직이 필요한 키 발급을 미리 받아서 요청 데이터 묶음을 처리&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;b&gt;&lt;u&gt;&lt;i&gt;&lt;span&gt; &lt;/span&gt;&lt;/i&gt;&lt;/u&gt;&lt;/b&gt;&lt;span&gt;할 수 있으면 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;CRM 을 하나의 플랫폼으로 바라본다면 OAuth2.0 의 프로토콜 방식을 생각해 볼 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;OAuth 의 Authorization Code Flow 는 Access Token 을 얻기 위해 code 값을 미리 받아서 이용하고 있습니다 해당 Flow 를 Sequence Diagram 으로 보면&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1223&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WEGWI/btsQ5jIHB5m/LXLxcaKTv61nzHtyo1kcv0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WEGWI/btsQ5jIHB5m/LXLxcaKTv61nzHtyo1kcv0/img.jpg&quot; data-alt=&quot;OAuth 2.0의 Authrization Code Grant Type Flow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WEGWI/btsQ5jIHB5m/LXLxcaKTv61nzHtyo1kcv0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWEGWI%2FbtsQ5jIHB5m%2FLXLxcaKTv61nzHtyo1kcv0%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;1223&quot; height=&quot;792&quot; data-origin-width=&quot;1223&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OAuth 2.0의 Authrization Code Grant Type Flow&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #050505; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;아래는 PortOne(구: 아임포트) 이라는 곳의 결제 Flow 입니다&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFF9eR/btsQ5ZC0iwY/NKA9z93cPIHESGhcB8e4i1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFF9eR/btsQ5ZC0iwY/NKA9z93cPIHESGhcB8e4i1/img.jpg&quot; data-alt=&quot;https://portone.gitbook.io/docs/auth/guide/def&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFF9eR/btsQ5ZC0iwY/NKA9z93cPIHESGhcB8e4i1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFF9eR%2FbtsQ5ZC0iwY%2FNKA9z93cPIHESGhcB8e4i1%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;1280&quot; height=&quot;1101&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1101&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://portone.gitbook.io/docs/auth/guide/def&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;뭔가 Authorization Code 와 위의 결제 키 라는 게 비슷하게 느껴집니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Kolon Mall(가맹점 중 하나) 과 CRM Point 와 User 의 결제창이라는것도 비슷한 관점이 있는것 같네요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 이와 같은 복잡한 구조를 가지게 되는데 아니 포인트 한번 써먹겠다고 인증 서버 수준의 구현을 따로 해야 되? 라는 논란의 여지가 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일단 ClientId 와 Client Evnet Id 라는 조합이 있기 때문에 token/billKey 발급을 위한 과정은 패스하고 추후 인증 서버를 소개할 때 다시 얘기하겠습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 우리는 플랫폼 인증이다 하면 위와 같은 행위가 필요할 수 있다고 보고 같이 고민해주시면 좋을 것 같네요 해당 코드나 키값으로 후속 처리를 쉽게 처리할 수 있습니다 (처음이 어려우면 나중이 쉬워지는 법!!)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;다시 고유 식별자로 넘어가면 KSUID 말고도 고유 식별자를 생성할 수 있는 방법은 어떤것이 있을까요?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=276041776&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;가상 면접 사례로 배우는 대규모 시스템 설계 기초&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 에서는 UUID 와 티켓서버와 다중 마스터 복제와 마지막으로 트위터의 스노플레이크 방식이 소개 되어 있습니다 티켓 서버와 다중 마스터 복제는 Auto Increment ID 가 대상이므로 패스 합니다(&lt;/span&gt;&lt;a href=&quot;https://velog.io/@broccolism/%EC%9D%98%EC%99%B8%EB%A1%9C-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9C%A0%EC%9D%BC-ID-%EC%83%9D%EC%84%B1%EA%B8%B0-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EA%B8%B0%EC%B4%88-7%EC%9E%A5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;관련 내용&lt;/span&gt;&lt;/a&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;KSUID&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;자연스럽게 생성 시간 순서로 정렬됩니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;충돌이 없습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단, 160bit 를 사용해서 UUID 의 128 bit 보다 큽니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uPh3F/btsQ67NEiaf/xrvM78TilKyOyQf27095S0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uPh3F/btsQ67NEiaf/xrvM78TilKyOyQf27095S0/img.jpg&quot; data-alt=&quot;KSUID&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uPh3F/btsQ67NEiaf/xrvM78TilKyOyQf27095S0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuPh3F%2FbtsQ67NEiaf%2FxrvM78TilKyOyQf27095S0%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;550&quot; height=&quot;896&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;896&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;KSUID&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;JDK 내장된 방법&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;UUID (Universally Unique Identifier)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;컴퓨터 시스템에 저장되는 정보를 유일하게 식별하기 위한 128비트 수 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;보편적으로 쉽게 사용할 수 있는 방법(다수의 언어거 기본적으로 제공하는 생성기) 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;숫자가 아닌 값이 포함될 수 있으며 옛날 버전의 경우 시간 순으로 정렬할 수 없다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;하지만 UUID v6, v7, v8 에서는 타임스탬프 기반으로 정렬이 가능해서 만들어 지고 있다고 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;38&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ym6Tr/btsQ6ezBFCV/DGEbkzT0zQZTkgHkDeWTVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ym6Tr/btsQ6ezBFCV/DGEbkzT0zQZTkgHkDeWTVK/img.jpg&quot; data-alt=&quot;uuid v4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ym6Tr/btsQ6ezBFCV/DGEbkzT0zQZTkgHkDeWTVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fym6Tr%2FbtsQ6ezBFCV%2FDGEbkzT0zQZTkgHkDeWTVK%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;331&quot; height=&quot;38&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;38&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;uuid v4&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가상 면접 사례로 배우는 대규모 시스템 설계 기초(책)&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fgithub.com%2Ftwitter-archive%2Fsnowflake&amp;amp;h=AT33mk3xXXBRHZ0DrRapj37iTt6cNy5RCYTpBhz8h8bvNg7ghlH1UE-dRjh0DX44Qp7EPJm5hFhJascNUbSiIh-NCgtOVz_R7va1TrfcbybTl6K_ea6pyNKQKIkVxsdtCHkdW1d5F_Z5bo6O0oyIjQ&quot;&gt;&lt;b&gt;&lt;span&gt;Twitter&amp;rsquo;s Snowflake&lt;/span&gt;&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;span&gt; (업데이트가 안되고 있습니다)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;989&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BUXaw/btsQ5V74yI2/zbBSKH9Bngn2kSH5IoLLB1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BUXaw/btsQ5V74yI2/zbBSKH9Bngn2kSH5IoLLB1/img.jpg&quot; data-alt=&quot;https://en.wikipedia.org/wiki/Snowflake_ID&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BUXaw/btsQ5V74yI2/zbBSKH9Bngn2kSH5IoLLB1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBUXaw%2FbtsQ5V74yI2%2FzbBSKH9Bngn2kSH5IoLLB1%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;1280&quot; height=&quot;989&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;989&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://en.wikipedia.org/wiki/Snowflake_ID&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;총 64bit 를 사용합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;도메인 주도 개발 시작하기(책 - 저자 최범균)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fzelark.github.io%2Fnano-id-cc%2F&amp;amp;h=AT33mk3xXXBRHZ0DrRapj37iTt6cNy5RCYTpBhz8h8bvNg7ghlH1UE-dRjh0DX44Qp7EPJm5hFhJascNUbSiIh-NCgtOVz_R7va1TrfcbybTl6K_ea6pyNKQKIkVxsdtCHkdW1d5F_Z5bo6O0oyIjQ&quot;&gt;&lt;b&gt;&lt;span&gt;Nano Id &lt;/span&gt;&lt;/b&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;엔터티의 식별자 생성 용도로 소개되었습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;UUID 보다 사이즈도 작고 생성기 속도도 빠르다고 합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;UUID 대안으로 더 가볍고, URL 친화적인 ID를 생성하는 라이브러리입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;JavaScript 가 메인 타겟이지만, 다양한 언어 구현체도 존재하고 이 녀석은 사용하는 문자의 범위를 지정하고, 사이즈를 가변적으로 선택할 수 있다는 장점이 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HLCy6/btsQ4MD1sca/cDlH8ncxn2z37YpxfGD45K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HLCy6/btsQ4MD1sca/cDlH8ncxn2z37YpxfGD45K/img.jpg&quot; data-alt=&quot;nano id&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HLCy6/btsQ4MD1sca/cDlH8ncxn2z37YpxfGD45K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHLCy6%2FbtsQ4MD1sca%2FcDlH8ncxn2z37YpxfGD45K%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;776&quot; height=&quot;56&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nano id&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 밖에도&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fgithub.com%2Fulid%2Fspec&amp;amp;h=AT33mk3xXXBRHZ0DrRapj37iTt6cNy5RCYTpBhz8h8bvNg7ghlH1UE-dRjh0DX44Qp7EPJm5hFhJascNUbSiIh-NCgtOVz_R7va1TrfcbybTl6K_ea6pyNKQKIkVxsdtCHkdW1d5F_Z5bo6O0oyIjQ&quot;&gt;&lt;b&gt;&lt;span&gt;ULID (Universally Unique Lexicographically Sortable Identifier)&lt;/span&gt;&lt;/b&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;이 선택지의 주요 컨셉은 UUID의 단점을 개선하는데 초점이 맞춰져 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;UUID 128비트 구조와 호환하면서, 정렬이 가능하며, 특수문자를 포함하지 않아 URL에서 사용해도 안전합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3cRDQ/btsQ79jIiZ4/A2roVmvMxut0fa7MNK7kQK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3cRDQ/btsQ79jIiZ4/A2roVmvMxut0fa7MNK7kQK/img.jpg&quot; data-alt=&quot;https://blog.tericcabrel.com/discover-ulid-the-sortable-version-of-uuid/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3cRDQ/btsQ79jIiZ4/A2roVmvMxut0fa7MNK7kQK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3cRDQ%2FbtsQ79jIiZ4%2FA2roVmvMxut0fa7MNK7kQK%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;922&quot; height=&quot;498&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://blog.tericcabrel.com/discover-ulid-the-sortable-version-of-uuid/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이것 말고도 &lt;/span&gt;&lt;a href=&quot;https://github.com/paralleldrive/cuid2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;CUID&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 나 정말 다양하게 있는데요 (너무 많아서 소개하기는 패스~!!)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;상황에 따라 고려할 내용들이 몇개 보였습니다&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;id 의 사이즈(용량)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;키 생성 시간(성능)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;특수문자가 사용되는지 유무(이식성 및 확장 - URL 에서 사용도 할 때)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;외부 노출 허용정도(보안)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;Entity(JPA) 나 Casandra(NoSQL) 와 같은 DB에서 활용 용도 (비즈니스)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;용량, 성능, 확장, 보안, 비즈니스들을 고려했을 때 단순히 조사해서는 결론을 내기 어려워서 추가로 NPM Trend 도 보았습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;마지막으로 NPM Trend 결과 입니다 언어가 JS 방면으로 한정되겠지만 그래도 가장 인기있는 개발언어입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기서 Star 수는 nano id 가 가장 많네요&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3tzVi/btsQ6jOxHKE/Btzc4kMYcswvK3RyITzEpk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3tzVi/btsQ6jOxHKE/Btzc4kMYcswvK3RyITzEpk/img.jpg&quot; data-alt=&quot;https://npmtrends.com/ksuid-vs-nanoid-vs-ulid-vs-uuid&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3tzVi/btsQ6jOxHKE/Btzc4kMYcswvK3RyITzEpk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3tzVi%2FbtsQ6jOxHKE%2FBtzc4kMYcswvK3RyITzEpk%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;1280&quot; height=&quot;1126&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://npmtrends.com/ksuid-vs-nanoid-vs-ulid-vs-uuid&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;추가 참고 내용&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mimul.com/blog/id-generation-in-mysql/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;DB 테이블의 키인 ID 생성에 대한 방법 고찰&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://findstar.pe.kr/2022/10/14/resource-id-generation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;리소스의 고유한 식별자는 어떤 형식을 사용해야할까?&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/251&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;분산 처리 시스템에서 유일한 식별자를 만드는 방법(twitter snowflake)&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/geekculture/the-wild-world-of-unique-identifiers-uuid-ulid-etc-17cfb2a38fce&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;The Wild World of Unique Identifiers (UUID, ULID, etc)&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://javascript.plainenglish.io/uuid-vs-nanoid-vs-cuid-c4fc1502325b&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;UUID vs NanoID vs CUID&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ksuid/ksuid&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Ksuid: K-Sortable Globally Unique IDs&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/114</guid>
      <comments>https://hankkuu.tistory.com/114#entry114comment</comments>
      <pubDate>Sun, 12 Oct 2025 23:27:48 +0900</pubDate>
    </item>
    <item>
      <title>서버의 동시성 이슈와 Redis 를 이용한 처리 방법들</title>
      <link>https://hankkuu.tistory.com/113</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;조만간 OLO 마일리지라는 것이 생길 예정입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해당 마일리지(포인트) 처리 개발 과정에서 발생 되었던 서버의 동시성 이슈들과 해결 방법 그리고 그 후의 상에 대해 정리해보고자 합니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;첫 번째 - 포인트 잔액 갱신&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;포인트 처리 비즈니스에는 적립 / 적립 취소 / 사용 / 사용 취소 / 삭제(탈퇴 처리) 와 같은 경우가 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기서 적립의 경우는 SQS 를 이용한 하이브리드한 동기(SQS 전송 후 비즈니스 처리를 적립기에 위임하지만 응답에 unique key 를 담아 보내는 구조) 처리를 하고 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우아한 형제들의 포인트 처리를 벤치마킹 했어서 비교하자면 적립 취소 까지는 SQS 를 사용하고 있었고 현재 저희 비즈니스는 적립 큐만 사용하고 있어 큐 분기는 없어서 다른 비즈니스는 모두 디비를 통해 처리하고 있습니다 (이 부분은 적립 취소 큐를 따로 쓰거나 적립 큐에서 이벤트를 키로 분기 해도 될 것 같습니다)&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gj6cm/btsQ6U11HVM/nlUktFUY1G4Jp4sSWW0qe1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gj6cm/btsQ6U11HVM/nlUktFUY1G4Jp4sSWW0qe1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gj6cm/btsQ6U11HVM/nlUktFUY1G4Jp4sSWW0qe1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGj6cm%2FbtsQ6U11HVM%2FnlUktFUY1G4Jp4sSWW0qe1%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;677&quot; height=&quot;503&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(적립기) - &lt;/span&gt;&lt;a href=&quot;https://hankkuu.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Ktor 를 써 봤습니다&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어쨌든 포인트 코어 | 포인트 적립기 모두 잔액 갱신이 가능한 분산 환경 상황이었고 동시에 같은 요청이 오게 되면 포인트 잔액이 틀어지는 상황이 생겨났습니다&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FSzek/btsQ7wsT2EM/PLcUPVoPC1iQrcgdo5slZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FSzek/btsQ7wsT2EM/PLcUPVoPC1iQrcgdo5slZ0/img.jpg&quot; data-alt=&quot;분산 서버 환경 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FSzek/btsQ7wsT2EM/PLcUPVoPC1iQrcgdo5slZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFSzek%2FbtsQ7wsT2EM%2FPLcUPVoPC1iQrcgdo5slZ0%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;1280&quot; height=&quot;824&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분산 서버 환경 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코드로 보자면&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760273939478&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val requestPoint = (...)
 
val balance = getRedisBalance(key)
val updateBalance = balance + requestPoint
 
setRedisBalance(key, updateBalance)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;적립 Worker 도 N 개 이고 포인트 Service Worker 도 N 개 입니다 이때 동시 요청들이 get 하고 set 할 타이밍에 get 에서 같은 값이 읽히게 되면 처리하지 못하는 적립 요청이 생기게 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;get ~ (사칙연산) ~ set 할 타이밍에 찰나의 유실이 발생됩니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;해결방법&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;개발자 방에도 문의해봤었고 점심식사 하기 전 엘레베이터 앞에서 충섭 팀장님께 Redis 동시성 처리를 문의 했었는데 그 때 잠깐 Redis 의 Incr 이라는 Command 에 대해 듣게 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daIq4T/btsQ7yjUvOp/0MdPRMgN0NP8Hvdn3eAwd1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daIq4T/btsQ7yjUvOp/0MdPRMgN0NP8Hvdn3eAwd1/img.jpg&quot; data-alt=&quot;퍼플아이오 개발자 대화방&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daIq4T/btsQ7yjUvOp/0MdPRMgN0NP8Hvdn3eAwd1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaIq4T%2FbtsQ7yjUvOp%2F0MdPRMgN0NP8Hvdn3eAwd1%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;849&quot; height=&quot;519&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;퍼플아이오 개발자 대화방&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pP6gi/btsQ6cBKIo2/6KsaRFoouBaszsSdJsXap1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pP6gi/btsQ6cBKIo2/6KsaRFoouBaszsSdJsXap1/img.jpg&quot; data-alt=&quot;출처 :&amp;amp;nbsp; https://www.slideshare.net/RedisLabs/atomicity-in-redis-thomas-hunter&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pP6gi/btsQ6cBKIo2/6KsaRFoouBaszsSdJsXap1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpP6gi%2FbtsQ6cBKIo2%2F6KsaRFoouBaszsSdJsXap1%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;1280&quot; height=&quot;640&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp; https://www.slideshare.net/RedisLabs/atomicity-in-redis-thomas-hunter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해당 연산을 Lettuce 라는 Redis Client 에서 지원을 해주어서 증감/감소 연산을 할 때 Atomic 한 동시성 처리가 매우 쉽게 가능해졌습니다&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYghgP/btsQ6WMk3Cm/OdzMo8UkCc3EbKEtErwEeK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYghgP/btsQ6WMk3Cm/OdzMo8UkCc3EbKEtErwEeK/img.jpg&quot; data-alt=&quot;Spring Code 단, Ktor 에서는 약간 다르지만 원리는 같습니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYghgP/btsQ6WMk3Cm/OdzMo8UkCc3EbKEtErwEeK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYghgP%2FbtsQ6WMk3Cm%2FOdzMo8UkCc3EbKEtErwEeK%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;1280&quot; height=&quot;417&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Code 단, Ktor 에서는 약간 다르지만 원리는 같습니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 번째 - 포인트 동시 변경 처리&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;포인트가 변경되는 비즈니스로 아래와 같은 부분이 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;만료 포인트 즉시 만료시키기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;가용 포인트 확인해서 사용 시키기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용 포인트 취소시 취소한 히스토리(이력) 추가하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 경우는 첫 번째 경우와 다르게 동시에 처리 하려는 Event(주문, 영수증발급, 포인트 차감/충전) 등 이런 상황이 동시에 처리하려고 하는 경우는 1건에 대해서만 유효한 것으로 인지하고 나머지 경우는 중복되게 처리하지 않기로 했습니다 (비즈니스 적으로 유효한지 안 한지 판단이 필요한 부분)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Mauve 의 포인트 설계는 자유 분방한 Client Event 이기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760274066444&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
  { group = 1, eventId = A, member = tom }, { group = 2, eventId = B, member = tom }
]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 형태로 되어 있었습니다 만약 아래와 같은 구조였다면 event 의 유일성이 더 보장되었을 것 같은데요&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760274086763&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  evnetId = A,
  points = [
    { group = 1, member = tom }, { group = 2, member =tom
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 구조가 아니기 때문에 Array 처리에 대한 모든 형태의 보장이 되야 했고 해당 포인트 들 모두 잘 처리해야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;동시에 여러 포인트 처리가 들어오면 모두 다 처리가 되기 때문에 사용자는 딱 한번만 처리 되길 원하는 상황에 동시에 여러번 들어오면 트랜잭션이 끝나기 전에 기존에 데이터를 읽어서 (Select ~ 이후 Insert ) 주문의 unique 가 보장되지 않고 여러번 처리됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 상황에서는 데이터베이스에서 처리와 어플리케이션에서 처리하는 것 에 대해 고려를 해봐야 하는데요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;Isolation Level과 Lock 이라는 것이 있어 우리가 데이터를 잘못 읽을 상황을 잡아줍니다&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;트랜잭션의 격리 수준(isolation)이란?&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;동시에 여러 트랜잭션이 처리될 때특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;트랜잭션 격리 수준은 어떤게 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;READ UNCOMMITTED (가장 낮음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;READ COMMITTED (현재 어플리케이션에서 사용하는 상태)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;REPEATABLE READ&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;SERIALIZABLE (가장 높음)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 내용이 현재 중요한 부분은 아니기 때문에 간략히 넘어갑니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가장 높은 경우로 하면 성능 측면에서는 동시 처리성능이 가장 낮기 때문에 성능이 필요한 경우에는 쓰지 않는 것으로 알고 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;Lock이란?&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터의 일관성을 보장하기 위한 방법입니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;Lock의 종류는?&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Lock은 상황에 따라서 크게 두가지로 나누어 집니다&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;Shared Lock(공유 Lock 또는 Read Lock)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;보통 데이터를 읽을 때 사용합니다 원하는 데이터에 lock을 걸었지만 다른 세션에서 읽을 수 있습니다 공유Lock을 설정한 경우 추가로 공유Lock을 설정할 수 있지만, 배타적 Lock은 설정할 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 내가 보고 있는 데이터는 다른 사용자가 볼 수 있지만, 변경할 수는 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2. Exclusive Lock(배타적 Lock 또는 Write lock)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;보통 데이터를 변경할 때 사용합니다 이름에서 느껴지는 것 처럼 해당 Lock이 해제되기 전까지는 다른 공유Lock, 배타적Lock을 설정할 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 읽기 기와 쓰기가 불가능하다는 의미입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Lock 을 사용해서 동시에 동일한 요청이 오는 경우 둘 중에 하나만 읽어서 처리하고 두 번째 요청에 대해서 동일한 현상이 발생되지 않게 처리할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;실제로 만료 포인트가 있는지 DB 에서 Select 를 해서 만료할 수치를 계산해서 Insert 를 이후에 하는데 Select 할 때 Lock 을 걸면 중복되어 소멸될 여지가 없기 때문에 문제가 해결됩니다 (JPA 기준 해당 Query 에 Lock 을 사용)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;당장 해결 하는 방식은 간단했으나 창수 팀장님께서 DB에 Lock 을 걸어 버리면 특히나 포인트의 모든 히스토리가 하나의 테이블로 관리되는 상황에서 의도치않은 Row Lock, Table Lock 이 생길 수 있고 성능이 저하 될 수 있어서 Redis 를 통한 분산 트랜잭션 처리에 대한 아이디어를 주셨습니다&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;i&gt;&lt;span&gt;마침 첫 번째 해결 방안에 대해 고민했을때 Redis 를 통한 분산락을 잡아 처리하는 방식에 대한 학습이 되어 있기 때문에 주저없이 Redisson 을 통한 분산락을 적용했습니다&lt;/span&gt;&lt;/i&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;관련 영상은 &lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EB%8F%99%EC%8B%9C%EC%84%B1%EC%9D%B4%EC%8A%88-%EC%9E%AC%EA%B3%A0%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;인프런에도&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 있어서 아마도 비슷할 것 같습니다 (그 만큼 대중적인 해결방법인듯)&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;해결방법&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;바로 코드로&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM3HJW/btsQ74bNI1g/dodcXGTECpH9a8b1t8fbEk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM3HJW/btsQ74bNI1g/dodcXGTECpH9a8b1t8fbEk/img.jpg&quot; data-alt=&quot;Redisson 을 통한 분산락 적용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM3HJW/btsQ74bNI1g/dodcXGTECpH9a8b1t8fbEk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM3HJW%2FbtsQ74bNI1g%2FdodcXGTECpH9a8b1t8fbEk%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;1280&quot; height=&quot;855&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Redisson 을 통한 분산락 적용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;원리는 간단합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;라이브러리에서 Lock 객체를 주기 때문에 해당 락을 잡고 락 유지 시간동안 executingFunction 에 비즈니스 처리를 넣습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해당 작업이 수행되기 전에 동시 요청 되는 LockName Event 에 대해 막습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;executingFunction commit 이 되고 나면 Lock 을 해소해서 그 순간의 동시 요청을 막습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;라이브러리가 다 해줘서 제가 한게 뭐지? 싶습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;아무튼 이걸로 해결이 다 될까요?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;세 번째 - 클라이언트의 잘못된 동시 반복 요청 자체를 차단&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;두 번째 과정으로 동시에 처리라는 것은 막을 수 있습니다 (즉 00:00 초 사이 순간적인 동시 처리가 막히는 거죠)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 문제는 처리가 끝나고 UnLock() 이 되는 상황에 발생됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;순간 동시 처리는 막히지만 UnLock() 이후에 클라이언트가 잘못된 요청을 다시 주면 동시 처리를 막지 못한 것처럼 처리되어 버린다는 &lt;/span&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;괴상한 논리구조&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;span&gt;가 생기게 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음의 동시에 처리는 막혔지만 두 번, 세 번의 동일 요청이 막히지 못하게 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사실상 클라이언트가 잘못된 요청을 동시에 많이주면 이 때는 처음 동시만 막고 그 이후는 처리가 되는 상황이됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 상황에서 창수팀장님이 incr 을 통해 키를 잡고 만료시간을 지정해서 키가 만료되는 동안 해당 요청을 막는 방법을 얘기하셨습니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;헤결방법&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;바로 코드로&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ORmTk/btsQ5VfURay/kKDJmFaJA5EtQznwkHhyuK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ORmTk/btsQ5VfURay/kKDJmFaJA5EtQznwkHhyuK/img.jpg&quot; data-alt=&quot;incr 을 이용한 만료시간 적용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ORmTk/btsQ5VfURay/kKDJmFaJA5EtQznwkHhyuK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FORmTk%2FbtsQ5VfURay%2FkKDJmFaJA5EtQznwkHhyuK%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;1280&quot; height=&quot;822&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;incr 을 이용한 만료시간 적용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #050505; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;마침 incr 은 학습이 된 상태라 Redisson 라이브러리는 처음 쓰는 것이지만 그래도 금새 적용해서 만료시간 이전에 동시 요청이 되는 경우도 막아 버렸습니다&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 5초간 동일 LockName 으로 오는 경우까지 다 막혀버립니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉 이미 동시처리 문제가 해소된 이후에도 방패를 바로 거두지 못하는 경우가 생기게 되는데 이때 executingFunction 이 처리되고 자동 만료가 된다면 또 잘못된 동시 요청을 허용하게 된다는 것이됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉 안전성을 위해서는 5초간은 잘못된 요청이 와도 막겠다가 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 부분을 좀 더 나이스하게 처리하려면 LockName 이 겹치지 않으면 의도하지 않은 Blocking 도 발생하지 않을 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;클라이언트 입장에서는 자체 에러 상황에 에러 복구를 위해 ReTry 시도가 생길 수도 있다는 것이지요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어쨌든 INCR 과 DB+REDIS 간 분산락을 잡음으로 DB 트랜잭션의 유일성이 보장되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;마지막으로&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;글 초반에 Client Event Id의 모호함으로 맘에 들지않는 JSON 구조에 대해 잠깐 얘기도 했었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그 밖에도 코어 서비스의 SaaS 비즈니스들의 확장 가능성에 대한 모호함으로 인해 중요한 값을 필수화 하려고 무리해서 Path Variable 로 올리면서 REST API URI 가 매우 맘에 들지 않는 부분도 캐치가 되었는데요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;또 거기에 더해서 초기 설계상 포인트의 키 시스템을 잘 도입했으면 하는 아쉬움도 더해지고 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;전에 빌링 파트는 아니지만 빌링 서비스 처리를 옆에서 보면서 BillKey 에 대해서 알고는 있었어서 포인트와 연결되는 BillKey(PointKey) 가 있는 것에 대해 고민들 해봐야 할것 같습니다 (아래는 단순한 하나의 예시)&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1uRRE/btsQ6MpgMBW/PaSas5oZPRHyrw8RqeQrdk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1uRRE/btsQ6MpgMBW/PaSas5oZPRHyrw8RqeQrdk/img.jpg&quot; data-alt=&quot;개인 사이드 프로젝트서 BillKey 사용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1uRRE/btsQ6MpgMBW/PaSas5oZPRHyrw8RqeQrdk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1uRRE%2FbtsQ6MpgMBW%2FPaSas5oZPRHyrw8RqeQrdk%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;1198&quot; height=&quot;890&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개인 사이드 프로젝트서 BillKey 사용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;span style=&quot;background-color: #ffffff; color: #050505; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/113</guid>
      <comments>https://hankkuu.tistory.com/113#entry113comment</comments>
      <pubDate>Sun, 12 Oct 2025 22:02:30 +0900</pubDate>
    </item>
    <item>
      <title>CI/CD Overview 스따뜨</title>
      <link>https://hankkuu.tistory.com/110</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;퍼플아이오에서 처음 시작하게된 된 업무가 생겼습니다. 어플리케이션을 빌드해서 원격지 서버에 배포하는 부분입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;젠킨스가 있다는 것은 알았지만 구축된 환경을 사용만 했기 때문에 직접 빌드나 배포를 수정하는 것은 알지 못했고 아.. 이것도 내가 알아야 하는 과목이구나 하지만.. 아직 미뤄둔 숙제 같은 느낌입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;제가 처음 Gitlab-CI 를 수정하게 된 이유는 Local Test Case 를 서비스에 따라 각각 약 100개~300개 정도 돌리는데 어디 하나 수정이 되어 나갈때&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;혹시나 딱 눈에 보이는 곳만 테스트하는 개발자 셀프 테스트만 되어 전체 테스트 코드를 돌리지 않고 나갈때&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 수정 사항이 잠재적인 버그가 되지 않기 위해 CI 에서 꼭 테스트 되어야 게으르고 꼼꼼하지 않는 제가 발 뻗고 잘 수 있는 방화벽을 만들어야 겠다 하는 바람이었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 삽질을 좀 겪으면서 어찌저찌 추가하게 되었고 가끔 CI 테스트 실패를 겪으면 꼼꼼하지 못한 저에게 채찍질을 할 명분이 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;운이 좋게 이번에 스터디가 열리면서 다양한 파트의 다양한 직군의 사람들과 각각의 처한 환경과 다양한 배포 환경을 접하게되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;서론이 길었는데 CI/CD에 대해 연구를 시작하게 되었고 스터디 산출물들이 업무에 적용되어 기존의 불편함이 하나 둘씩 반영될 예정입니다. 개선된 내용이 이어질 2탄~ 3탄 기대해주세요 Coming Soon! &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그 전에!! 핵심용어와 현재 상황과 개선점에 대해 공유하고자 해당 포스팅을 하게 되었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일단 지속적 소프트웨어 개발 방법이란 무엇일까요?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;바로 답 부터! 반복적인 코드 변경사항을 지속적으로 빌드, 테스트, 배포할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 반복적인 프로세스는 버그가 있거나 실패한 이전 버전에서 테스트를 반복하며 리뷰를 하고 완성형의 코드를 배포시키는데 도움이 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 방법을 사용하면 새 코드 개발부터 배포까지 사람의 개입을 줄이거나 전혀 개입하지 않으려고 노력합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;즉 지속적인 반복에 대해 자동화를 한다는 것 입니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;지속적 통합(Continuous Integration, CI)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;GitLab의 Git 저장소에 코드가 저장된 애플리케이션을 생각해 보십시오. 개발자는 매일, 하루에 여러 번 코드 변경사항을 푸시합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;리포지토리에 푸시할 때마다 애플리케이션을 자동으로 빌드하고 테스트할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 자동화된 스크립트는 애플리케이션에 오류가 발생할 가능성을 줄일 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 방법을 &lt;/span&gt;&lt;b&gt;&lt;span&gt;지속적 통합&lt;/span&gt;&lt;/b&gt;&lt;span&gt;이라고 합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;애플리케이션에 제출된 각 변경사항은 운영 뿐만 아니라 개발 브랜치에도 자동으로 지속적으로 빌드되고 테스트됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이러한 정책과 분산 환경의 변경사항이 애플리케이션에 대해 설정한 모든 테스트, 지침 및 코드 준수 표준을 통과하도록 보장합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;지속적 전달(Continuous Delivery, CD)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지속적 전달은 지속적 통합을 넘어서는 단계입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코드 변경이 코드베이스에 푸시될 때마다 애플리케이션이 빌드되고 테스트될 뿐만 아니라 애플리케이션도 지속적으로 배포됩니다. 그러나 지속적 전달을 사용하면 배포를 수동으로 트리거합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지속적 전달은 코드를 자동으로 확인하지만 변경사항의 배포를 수동으로 전략적으로 트리거하려면 사람의 개입이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;지속적 배포(Continuous Deployment, CD)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지속적 배포는 지속적 전달과 유사한 지속적 통합을 넘어서는 또 다른 단계입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;차이점은 애플리케이션을 수동으로 배포하는 대신 자동으로 배포되도록 설정한다는 것입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사람의 개입이 필요하지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxXXNs/btsQ72rxOKt/6UTaAsQgklkXJjcFXzTd7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxXXNs/btsQ72rxOKt/6UTaAsQgklkXJjcFXzTd7K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxXXNs/btsQ72rxOKt/6UTaAsQgklkXJjcFXzTd7K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxXXNs%2FbtsQ72rxOKt%2F6UTaAsQgklkXJjcFXzTd7K%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;873&quot; height=&quot;484&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Gitlab-CI 에서 알아야 하는 용어&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Pipeline&lt;/span&gt;&lt;br /&gt;&lt;span&gt;파이프라인은 지속적 통합, 전달 및 배포의 최상위 구성 요소&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Jobs&lt;/span&gt;&lt;br /&gt;&lt;span&gt;파이프라인 구성은 Job으로 시작됩니다. Job은 &lt;/span&gt;&lt;span&gt;.gitlab-ci.yml&lt;/span&gt;&lt;span&gt; 파일의 가장 기본적인 요소입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Variable&lt;/span&gt;&lt;br /&gt;&lt;span&gt;CI/CD 변수는 환경 변수의 한 유형입니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/ci/variables/predefined_variables/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;사전 정의된 CI/CD 변수의 기본 세트&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Environments&lt;/span&gt;&lt;br /&gt;&lt;span&gt;환경은 코드가 배포되는 위치를 설명합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Runners&lt;/span&gt;&lt;br /&gt;&lt;span&gt;GitLab CI/CD에서 러너는 &lt;/span&gt;&lt;span&gt;.gitlab-ci.yml&lt;/span&gt;&lt;span&gt;에 정의된 코드를 실행합니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;공유 러너(Shared runners)는 GitLab 인스턴스의 모든 그룹 및 프로젝트에서 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;그룹 러너(Group runners)는 그룹의 모든 프로젝트와 하위 그룹에서 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;특정 러너(Specific runners)는 특정 프로젝트와 연결됩니다. 일반적으로 특정 러너는 한 번에 하나의 프로젝트에 사용됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Artifacts&lt;/span&gt;&lt;br /&gt;&lt;span&gt;단계(Stages) 간에 전달되는 단계 결과에 사용합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;i&gt;&lt;span&gt;Cache&lt;/span&gt;&lt;/i&gt;&lt;/u&gt;&lt;br /&gt;&lt;u&gt;&lt;i&gt;&lt;span&gt;프로젝트 의존성(Dependencies)을 저장하는 데 사용합니다.&lt;/span&gt;&lt;/i&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;.gitlab-ci.yml 파일 생성&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;.gitlab-ci.yml&lt;/span&gt;&lt;span&gt; 파일은 GitLab CI/CD에 대한 특정 지침을 구성하는 YAML 파일입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 파일에서 다음을 정의합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;러너가 실행해야 하는 작업(Job)의 구조와 순서&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;특정 조건이 발생할 때 러너가 내려야 하는 결정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;사전 조건&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;프로젝트에서 사용할 수 있는 하나 이상의 Runner가 등록되어 있어야 합니다.&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;Settings &amp;gt; CI/CD&lt;/span&gt;&lt;/b&gt;&lt;span&gt;의 &lt;/span&gt;&lt;b&gt;&lt;span&gt;Runners&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 섹션에서 확인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJy8mY/btsQ5X5TzkG/RBdE9BtMhieH214xP9Am20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJy8mY/btsQ5X5TzkG/RBdE9BtMhieH214xP9Am20/img.jpg&quot; data-alt=&quot;Gitlab-CI 에서 사용하는 러너&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJy8mY/btsQ5X5TzkG/RBdE9BtMhieH214xP9Am20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJy8mY%2FbtsQ5X5TzkG%2FRBdE9BtMhieH214xP9Am20%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;537&quot; height=&quot;455&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Gitlab-CI 에서 사용하는 러너&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Gitlab-Ci.yml 파일은 선언형 방식으로 코드를 추가해 나갑니다 상세 내용은 gitlab 회사의 공식문서에 나온 용어 부터 시작해서 뻗어나가는 것을 추천합니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;CI Build 에 대한 방식 &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Build 프로세스에서 Docker Image 로 생성하고 활용하는 것을 적극적으로 사용하고 있습니다 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우리가 사용하는 방식 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Spring Boot App&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;gradle plugin (현재 CRM 파트에서 사용하는 형태)&lt;/span&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://github.com/GoogleContainerTools/jib&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Jib로 자바 컨테이너 빌드&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; ./gradlew jibDockerBuild&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Dockerfile 생성&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://workshop.infograb.io/gitlab-ci/33_add_docker_build_stage/1_create_dockerfile/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Dockerfile을 생성합니다&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://workshop.infograb.io/gitlab-ci/33_add_docker_build_stage/2_add_build_stage/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;gitlab-ci.yml 파일에 Docker 이미지 빌드 단계 추가&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Docker Registry Service&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonECR/latest/userguide/docker-push-ecr-image.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;AWS ECR 서비스&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;CI Process 에 대한 방식&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt; 각자 작업한 branch 에서 push 후 Merge Request 를 요청합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;MR 리뷰 후 -&amp;gt; 담당자가 MR을 머지하면 CI가 자동으로 동작합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CI가 동작한다는 것은 Build - (test) 과정을 진행하고 Deploy Stage 가 진행됩니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;현재 프로젝트에서 Deploy 되는 것은 AWS ERC 에 올리는 것으로 지속적 전달(CD)로 진행됩니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;&lt;u&gt;&lt;span&gt;ArgoCD에 접속해서 로그인 후 해당 어플리케이션의 상대를 Refresh 하고 Sync 를 맞춥니다&lt;/span&gt;&lt;/u&gt;&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;배포가 되었으면 각각의 환경에 맞게 스테이지/운영 테스트를 진행 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;개발환경에 대한 고민&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;저는 개발환경이 각각의 외부 연동과 협업 상황에 맞게 분리되어야 한다고 생각합니다. (4~5개 정도)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 내용을 얘기하면 길어 질 것 같고 글로벌 서비스가 아니라 Admin 개발에 오버엔지니어링 일 수 있다는 반론도 수용되기 때문에 관련 링크만 첨부합니다. (조대협 - &lt;/span&gt;&lt;a href=&quot;https://bcho.tistory.com/759&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;개발환경&lt;/span&gt;&lt;/a&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;환경이 많아지면 Git Flow 방식도 고려 대상이 됩니다 환경에 따라 각각 Branch Main Stream 이 구분되어 생성될 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCOmkF/btsQ5bQ9Ujd/kvaA08UYeETjk5Q65fNdM0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCOmkF/btsQ5bQ9Ujd/kvaA08UYeETjk5Q65fNdM0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCOmkF/btsQ5bQ9Ujd/kvaA08UYeETjk5Q65fNdM0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCOmkF%2FbtsQ5bQ9Ujd%2FkvaA08UYeETjk5Q65fNdM0%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;873&quot; height=&quot;336&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Development&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Code&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Commit&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CI Pipeline&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Build&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Test&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Unit Test&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Integration Test&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CD Pipeline&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;QA&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Staging&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Review&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Deploy&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Production&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대신 그림으로 보자면 이 일련의 과정이 수행되는 환경이 구분이 분명 될 것이고 자동화된 CI는 개발자에게 새벽 배포가 아닌 충분한 숙면을 줄 수 있을 것입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;본문으로 생각한 내용은 여기까지고 추후에 이어질 성능개선 된 CI/CD 내용도 같이 기대해주세요&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;힌트는 기울여진 글씨들에 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;ArgoCD 와 성능 개선된 부분에 대해서는 다시 게시될 예정입니다 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/110</guid>
      <comments>https://hankkuu.tistory.com/110#entry110comment</comments>
      <pubDate>Sun, 12 Oct 2025 21:50:25 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot 3.0 Release Notes</title>
      <link>https://hankkuu.tistory.com/109</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Kotlin Spring 개발을 하면서 꾸준히 라이브러리 / 프레임워크 / gradle 버전 및 관련 플러그인들 버전을 체크하고 올리고 있는데 Spring의 메이저 버전이 새롭게 Release 되었네요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring 5 -&amp;gt; 6&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring boot 2.7 -&amp;gt; 3.0&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes&quot;&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2.7 버전 아래에서는 2.7 버전으로 올리라는 추천 문구부터 JDK 17을 최소버전으로 8 -&amp;gt; 17 로 확 올리다니 11 버전은 뭔가 동일선상의 LTS 에서 붕 뜬 느낌이네요 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다행히 기존의 프로젝트들도 11에서 17로 올렸고 (Mauve) 신규 CRM도 JDK 17부터 하는 걸 제안해서 하고 있었습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;kotlin 버전도 1.7.2로 올려야 할 것 같네요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아직 스프링 개발자라고 하기 뭣하기가 스프링의 코어 부분에 대한 지식이 매우 없네요 ㅎㅎ.. (뭐 다른것도 아장아장 걸음마 이지만)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아키텍처를 소개해드리고 싶은데 아래 그림이 해석이 좀 어려운데 공부 겸 한번 시작해보자면&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v8EXx/btsQ62ekRCL/gKsIBSgThp8XZky6cKs7H1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v8EXx/btsQ62ekRCL/gKsIBSgThp8XZky6cKs7H1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v8EXx/btsQ62ekRCL/gKsIBSgThp8XZky6cKs7H1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv8EXx%2FbtsQ62ekRCL%2FgKsIBSgThp8XZky6cKs7H1%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;1280&quot; height=&quot;718&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;3.0에서는 AOT 플러그인 제공과 Native 지원에 관한 부분이 확대된다는 내용이라고 합니다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;(그림의 Spring Boot 3 과 Spring Framework 6)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring 6.0을 통해 AOT transformation engine도 더 좋아진 버전의 엔진이 사용되는 것이고 Native에 굉장히 힘을 쏟는 것이라는 설명으로 보아 자동차 엔진의 성능이 더 좋아졌다고 보면 될 듯 합니다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;(그림의 Spring portfolio )&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;엔진이 좋아지면 어떤게 좋아지느냐? 이게 이 고성능 외제차를 사야하나 말아야 하나의 관건 인데요 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AOT의 적용 효과라고 합니다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;런타임시 Spring 인프라를 적게 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;런타임 시 검증할 조건 수 감소&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리플렉션을 줄이고 프로그래밍적 Bean 등록 방식 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 내용을 볼때 런타임/리플렉션 이런 부분이 어플리케이션의 런타임 성능을 높이려는 주안점으로 보입니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;엔진 내부를 조금 더 보면 (AOT : Ahead Of Time) &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsrO3h/btsQ5BPwEqX/9OltKk8i62aXhhiCyL9JI1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsrO3h/btsQ5BPwEqX/9OltKk8i62aXhhiCyL9JI1/img.jpg&quot; data-alt=&quot;Spring Boot 3.0 AOT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsrO3h/btsQ5BPwEqX/9OltKk8i62aXhhiCyL9JI1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsrO3h%2FbtsQ5BPwEqX%2F9OltKk8i62aXhhiCyL9JI1%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;1280&quot; height=&quot;573&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;573&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Boot 3.0 AOT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #050505; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Spring AOT 엔진은 빌드 시 스프링 애플리케이션을 분석하고 최적화하는 도구이고&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AOT 엔진은 GraalVM Native Configuration이 필요로 하는 reflection configuration을 생성해줍니다(그림에는 없음) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이것은 Spring native 실행 파일로 컴파일 하는데 사용 되고 이후에 애플리케이션의 시작 시간과 메모리 사용량을 줄일 수 있게 됩니다 (&quot;Native&quot; 라는 성능 친화적인 이름값을 하려는 것으로 보입니다) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring 의 Native-Image compiler 이 녀석을 통해 빌드 시간은 길고, 시작 시간이 짧고 메모리는 적게 사용게 된다는 부분이 있습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nYjuM/btsQ5zc5s62/WKHEytCL0hBBfMR17Sqv41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nYjuM/btsQ5zc5s62/WKHEytCL0hBBfMR17Sqv41/img.jpg&quot; data-alt=&quot;AOT 확대&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nYjuM/btsQ5zc5s62/WKHEytCL0hBBfMR17Sqv41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnYjuM%2FbtsQ5zc5s62%2FWKHEytCL0hBBfMR17Sqv41%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;1280&quot; height=&quot;704&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;704&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AOT 확대&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;위 그림에서 보면 AOT가 Spring Boot 환경에서 하는 일들과 순서를 알 수 있습니다&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;간단하게 얘기하자면 Bytecode를 분석하고 최적화해서 좀 더 실행하기에 빠르고 메모리적으로 효율적인 코드를 만듭니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 위에서 GraalVM 에 대한 부분도 보이는데.. 이걸 얘기하자니 JVM과 JIT(just in time) 컴파일러 부터 시작해야 하는 부분이라... 난감합니다 어쨌든 기존에 너무 너무 느리고 안좋으니 방식을 바꾸려고 노력했다 라고 해야 할 것 같은데요 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/AOT_%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Just In Time Compiler&lt;/span&gt;&lt;/a&gt;&lt;span&gt; : 중간 언어를 실행 시점에 번역하는 인터프리터 방식 (기존 방식)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/JIT_%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Ahead Of Time Compiler&lt;/span&gt;&lt;/a&gt;&lt;span&gt; : 중간 언어(자바 바이트 코드)를 미리 목표 시스템에 맞춰 번역하는 방식&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;의 차이가 있고 추가적인 내용은 NHN 에서 리서치한 내용을 참고로 더 보셔도 좋을 것 같습니다 (&lt;/span&gt;&lt;a href=&quot;https://meetup.nhncloud.com/posts/273&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;NHN 링크&lt;/span&gt;&lt;/a&gt;&lt;span&gt;) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이쯤에서 다시 러닝 커브가 오게 되었는데... (아니 그러면 기존의 Open JDK 사용 진영은? GraalVM 쓰지 않으면 그냥 느리게 지내라는 것인가? 기존의 JIT 컴파일러는 노답이라는 건가?) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지금 이런 노력을 왜 하는지 히스토리를 좀 알아야 하는 부분이 있는데요 현재 사용중인 JVM이 성능 한계를 만났기에 Spring Native 와 같은 시작을 하게 되었다고 하네요&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 말이 뭐지? &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하는 분들은 Spring 개발이 다른 프레임워크나 언어로 하는것에 비해 서버 실행 시간이나 속도, 메모리 사용량이 많다는 것을 생각해 주셨으면 합니다 (Spring 은 구리다는 단점 들 중)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그럼에도 불구하고 오랜시간 축적된 다양한 기능과 안정성 그리고 컨벤션에 의한 대규모 개발이 가능했기 때문에 단점들이 커버되어서 널리 사랑받았다는 평도 본적이 있었습니다 (대규모 인력 개발로서는 대체제가 있을까? 공감되는 부분) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그 밖의 세세하게 변경된 부분을 보자니 내용이 더 길어질 것 같습니다 (이 부분은 업그레이드를 하게 되면 2탄으로 정리해보겠습니다) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그러고 보니 어플리케이션 테스트 코드 짜는 가이드 방법도 마지막 글을 적어야 하는 군요.. ㅎㅎ (1탄, 2탄, 마지막)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어쨌든 새로운 엔진을 교체하면서 AOT를 적용하려면 해당 플러그인도 설치를 해야 할 것 같습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Gradle 에 새로운 Configuration 을 추가해야 할 수도 있고요 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아직은 0살 버전이라 어느 시니어 개발자 분 우스겟 소리로는 3살은 넘어야 쓸 수 있는 버전이라고 했던 기억도 나서 일단은 변경 부분에 대해 주시해 보려고 합니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;성능을 위한 코어 개발자들의 노력들이 대단 한 것 같습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;관련 내용 링크&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes&quot;&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/spring-tips/spring-boot-3-aot&quot;&gt;https://github.com/spring-tips/spring-boot-3-aot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://meetup.toast.com/posts/273&quot;&gt;https://meetup.toast.com/posts/273&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/AOT_%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;AOT 컴파일&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/JIT_%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;JIT 컴파일&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marrrang.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Spring 6.0 과 Spring Boot 3.0에는 뭐가 달라질까&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@jaehyunup/%EB%8B%A4%EA%B0%80%EC%98%A4%EB%8A%94-Spring-6.0-Boot-3.0-%EB%A6%B4%EB%A6%AC%EC%A6%88%EC%97%90%EC%84%9C%EC%9D%98-%EC%A3%BC%EB%AA%A9%ED%95%A0%EB%A7%8C%ED%95%9C-%EB%B3%80%EA%B2%BD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;다가오는 Spring의 메이저 업데이트 - Spring 6.0 , Boot 3.0에서 주목할만한 변경점은 어떤것이 있을까요? (출시일, 릴리즈노트 정리)&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/109</guid>
      <comments>https://hankkuu.tistory.com/109#entry109comment</comments>
      <pubDate>Sun, 12 Oct 2025 21:48:52 +0900</pubDate>
    </item>
    <item>
      <title>서버의 Error 에 따른 HTTP Status Code</title>
      <link>https://hankkuu.tistory.com/108</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;공통적인 응답을 고민하는 부분에서 예외가 발생하면 처리 해야하는 부분에 대해 정리 했습니다 &lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;200(or 204) vs 404 HTTP Status 논쟁&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일단 Java Excetpion 의 구조는 아래와 같습니다 &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deH4St/btsQ6UOuUAE/WjBsoIVkVw3VyMttGPHy80/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deH4St/btsQ6UOuUAE/WjBsoIVkVw3VyMttGPHy80/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deH4St/btsQ6UOuUAE/WjBsoIVkVw3VyMttGPHy80/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeH4St%2FbtsQ6UOuUAE%2FWjBsoIVkVw3VyMttGPHy80%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;652&quot; height=&quot;434&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Error 는 비정상적인 상황에서 발생하고 시스템 레벨에서 발생하는 심각한 수준의 오류이기 때문에 개발자가 할 수 있는 방법이 거의 없습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그에 반해 Exception 은 개발자가 직접 처리하는 코드에서 발생되는 예외 들로서 예외를 잡아서 정상 처리를 하거나 클라이언트에 통보 할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GLIdI/btsQ7uV9avR/yGCFf2HKRUSxsYFGagslBK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GLIdI/btsQ7uV9avR/yGCFf2HKRUSxsYFGagslBK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GLIdI/btsQ7uV9avR/yGCFf2HKRUSxsYFGagslBK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGLIdI%2FbtsQ7uV9avR%2FyGCFf2HKRUSxsYFGagslBK%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;797&quot; height=&quot;167&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Exception 을 처리 할 때 상황에 따라 예외발생 후 복구가 가능합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들면 Rollback 도 정상적으로 처리 하지 못해 예외 발생 후 복구라고 할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 현실에서는 예외가 발생 후 꼭 복구가 가능하다고 볼 수는 없습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예외에 대해 복구를 진행하는 것이 아닌 문제에 대해 Exception 을 발생하고 그냥 다시 입력을 가하게 하거나 기본값을 셋팅 해서 예외 발생시에는 기본값으로 진행하게 해서 후속 조치를 받거나 하게 할 순 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;하지만 Exception 이 발생되지 않는 다면?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Client 는 예외 상황이 발생 되었고 후속 조치에 대해 어떻게 반응 할 것인가? 이런 의문이 듭니다 (&lt;/span&gt;&lt;b&gt;&lt;span&gt; 뭐 아무것도 안 하면 서로 편한거 아닌가? 란 반론은 들 수 있겠지만&amp;hellip;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, &quot;Not found&quot; or &quot;Duplicate&quot; 와 같은 있거나 없는 상황에 시스템이 그냥 가만이 있을 것인지 아니면 뭔가 프로세스 상 예외라고 볼 수 있는 상황인지에 대해 근본적으로 묻고 싶어 집니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;실제 이 부분에서 HTTP API 의 Resource 에 대해&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;204(No Content), 200(OK but Empty)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;400(Bad Request), 404(Not Found)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처리에 대한 의견이 불분명해지고 HTTP API or HTTP Request 를 어떻게 구분 할지에 대한 혼란도 생겼습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Resource 를 URI 경로와 data 자원 으로 두 가지 형태로 볼 때 브라우저(클라이언트) 입장에선 자원이 웹 페이지 경로고 존재하지 않는 경로(자원)를 요청했기 때문에 404 상태 코드를 응답했기 때문입니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQy50x/btsQ5U2nC3j/2HkVD9mxXbsIkfg9ndXx6K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQy50x/btsQ5U2nC3j/2HkVD9mxXbsIkfg9ndXx6K/img.jpg&quot; data-alt=&quot;404 Not Found&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQy50x/btsQ5U2nC3j/2HkVD9mxXbsIkfg9ndXx6K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQy50x%2FbtsQ5U2nC3j%2F2HkVD9mxXbsIkfg9ndXx6K%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;696&quot; height=&quot;283&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;404 Not Found&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래의 글을 읽어 볼까요 &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/px4h3/btsQ6cIu8CP/oYunDgRTxhHfTYuixc6vW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/px4h3/btsQ6cIu8CP/oYunDgRTxhHfTYuixc6vW0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/px4h3/btsQ6cIu8CP/oYunDgRTxhHfTYuixc6vW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpx4h3%2FbtsQ6cIu8CP%2FoYunDgRTxhHfTYuixc6vW0%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;1008&quot; height=&quot;511&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기서 말하는 명분은 REST 입장에서 생각을 해야 한다는 부분을 주장합니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;이쯤에서 한번 REST 의 용어 풀이에 대해 짚고 넘어 가보겠습니다&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;asciidoc&quot; style=&quot;background-color: #fafbfc;&quot;&gt;&lt;code&gt;- REST 약어는 특정한 상태의 자원에 대한 표현 즉 REpresentational State Transfer (표현된 (자원)의 상태 전송)
- 자원의 상태에 대해 논하고 있고 이 자원은 분명 있을 수도 없을 수도 있다&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그렇다면 결국은 프론트 쪽에 미안해지지만 브라우저에서 URL로 화면에 안 나온다고 Not Found 만 되는걸로 보는게 아니라 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프론트에 사용 된 브라우저는 단지 다양한 클라이언트 중에 하나일 뿐..&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;경로 못 찾았다고 404 보여준것과 리소스 상태가 없거나 찾을 수 없는 경우는 화면과 관계없는 404로 헷갈림을 준다고 한다면 브라우저 위주의 에러 페이지 보여주기 처리에서 404 에러 페이지 라우팅이 HTTP Status 404를 전체하는가?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제는 과거의 백엔드가 화면을 컨트롤 하던 웹 개발과 달리 프론트 자체에서 화면 라우트가 직접 제어가능 하도록 API 서버에서 분리되어 자유롭게 기능이 많이 위임되었습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제는 HTTP Status 값에 있어 경로 없는거랑 REST API 상태랑 똑같이 보면 안되지 않나 생각이 앞섭니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;그렇다면 아래와 같은 풀이를 해보자면 &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음의 Exception 으로 와서 위에 분명 리소스의 상태에 대해 있을 수도 or 없을 수도 있다고 했습니다 즉 아래와 같은 풀이가 가능하다면 &lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #fafbfc;&quot;&gt;&lt;code&gt;있을 거라 생각했는데 예상 못하고 없는 경우?! 
	- 400(Bad Request)
	- 404(Not Found)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;background-color: #fafbfc;&quot;&gt;&lt;code&gt;없을 수도 있다고 생각했는데 진짜 없는 경우!?
	- 204(No Content)
	- 200(OK but Empty)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;말 장난 같긴 한데&amp;hellip; 이게 필요하다고 생각한 이유는 200, 204는 예외로 보지 않는 다는 것이라서 짚은 것 입니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;처음에 가진 궁금증&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 Exception 이 발생되지 않는 다면?&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Client 는 예외 상황이 발생되었고 후속 조치에 대해 스스로 예외 복구를 하는 것과 같은 [있다]/[없다]에 대한 인지 부조화를 타개할 수가 있는지 대한 의문이 든다는 점 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Not found or Duplicate 와 같은 있거나 없는 상황에 시스템이 그냥 가만이 있을 것인지 아니면 뭔가 프로세스 상 예외라고 볼 수 있는 상황인지에 대해 근본적으로 묻고 싶어 진다 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 부분에서 뭔가 클라이언트 스스로 응답에 대해 판단하고 처리 할 수 있겠지만&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;서버에서 무조건 예외를 만들어 내 뱉는 오버 헤드를 겪더라도 클라이언트에 직접 뭐라도 해라 라고 알려줬다는 안도감으로 봐야 하는지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래 글을 읽고 곱씹어 보자..&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sFClq/btsQ6KyDcRa/mgGEcZB5SCrK0aKsYktsU0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sFClq/btsQ6KyDcRa/mgGEcZB5SCrK0aKsYktsU0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sFClq/btsQ6KyDcRa/mgGEcZB5SCrK0aKsYktsU0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsFClq%2FbtsQ6KyDcRa%2FmgGEcZB5SCrK0aKsYktsU0%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;780&quot; height=&quot;219&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예외를 Backend 에서 잡아 처리했을 때 정보를 &quot;전달&quot; 하는 역할에서 Runtime Exception 을 발생하는 명분이 된다면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로세스가 복잡한 화면들인 경우(대출? 구매? 포인트 사용) 예외를 강제함으로 로직의 흐름이 올바르지 않을 때 적절하게 끊어 갈 수 있는 부분을 강제한다면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 이런 경우 400 응답을 명시하게 되었고 404의 경우는 경로 없는 거랑 헷갈리고 싶지 않다고 하는 의견도 있어 타협할 수 있는 Bad Request 로 처리하기도 했습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(그런데.. API Backend 는 경로 관여 안 하는 데 언제까지 이 논리를 봐줘야 하는지..)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이렇게 보면 클라이언트(브라우저를 비롯)는 데이터 조작과 같은 행위를 할 때는 해당 Command 를 바로 실행하지 않고 &lt;/span&gt;&lt;b&gt;&lt;span&gt;(돌 다리도 두드려 보고 행위를 시작해야 합니다)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 지금 이 행위의 대상인 리소스가 제대로 되어 있는지 없는지 Get Method로 확인 하고 해야 Exception을 처리한 Error Response 를 만나지 않게 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이러면 Request 단위가 ([Get] -&amp;gt; [Command]) 로 2번으로 묶여버리면 프론트 입장에서는 매우 귀찮을 수 있고 항상 2 Thread 자원을 써야 하는 Spring Boot MVC 는 더 많은 사람을 받는 처리가 어려워 지지 않는 다는 고민도 해볼 수 있습니다 (성능 테스트를 직접 해야 하나.....)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위에 돌 다리도 두드려 보고 가라와 같은 워딩을 몇 개 더 해보자면&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;마음대로 하지 말고 (백엔드에) 물어 보고 해라&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;그냥 막 하지 말고 정확한 것만 해라&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;모르는 상태로 하지 말고 제대로 알고 해라&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;자 이제 이걸 Backend 에서 보장해 주는 것이라고 생각 한다면&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #fafbfc;&quot;&gt;&lt;code&gt;2번 하지마세요~ Backend 에서 있는 경우만 완벽하게 보장해서 처리해 줄게요 
때로는 재시도까지 몇번 시도해서라도 꼭 되게 해줄게요 
대신에 그럼에도 불구하고 안되면 왜 안되는 지 클라이언트에서도 한번 다시 체크하거나 처음부터 다시 해주세요&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이럴려고 4xx 대 오류를 불가피 하게 넘겨주는 경우가 된다면 조금 수긍이 될까요?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;너무 오랫동안 이 부분에 대해 고민했는데.. 일단 소규모 조직이라면 쌍방이 협의하는게 제일 정확할 것 같긴합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다만 너무 논리적으로만 생각하려고 해서 HTTP의 본질을 놓쳤을까봐 아래 대응을 추가로 남깁니다&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mEw6a/btsQ7x6oOnw/FOZ2OCYvZPvQBlMkdxQC0K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mEw6a/btsQ7x6oOnw/FOZ2OCYvZPvQBlMkdxQC0K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mEw6a/btsQ7x6oOnw/FOZ2OCYvZPvQBlMkdxQC0K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmEw6a%2FbtsQ7x6oOnw%2FFOZ2OCYvZPvQBlMkdxQC0K%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;623&quot; height=&quot;661&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기서 URI Path 경로와 Query String에 의한 질의가 있는 경우를 얘기해서 해석이 들어가는데&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;http://localhost:8080/api/goods/:id?type=3&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;id 까지는 존재하는데 query string 에의한 type=1이 없다면 resouece는 존재하는 것으로 보고 200에 empty 로 처리한다고 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그러면 id로 찾았는데 없다면!!! 이 때는 확실히 4xx NOT FOUND를 명시하면 될까요? resource가 uri 상 없는게 확실한 경우가 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결론을 보면 둘 다 비즈니스에 따라 네이밍 된 HTTP API URI 설계 상황에 따라 쓰인다가 맞는것으로 보입니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;그러니 API 문서를 작성은 무조건 합시다! (잘 설계하는 것도 포함)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일관성 관점에서 클라이언트간 협의가 된다면 HTTP URI Path 에 존재 여부에 따라 2xx / 4xx 이 분기되고 query string 까지 안 찾아지는 경우는 2xx 으로 정상 처리하는 것으로 제안 해봅니다&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;당연히 프론트에서도 있다/없다 판단하고 스스로 완성할 수 있다는 것은 알고 있어요.. 그래도 우리 커뮤니케이션 맞추고 했으면 하는 바람이 있었어요.. ㅠㅠ&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;[추가 적인 제안]&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Http status 는 200,400,500 3개만 있는게 아니에요.. 우리 다같이 공부해야 할 것 같아요... ㅠㅠ&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;201, 204, 401, 403, 404 말고도 알아야 하지 않을까? 하는 것들&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;202 : Accepted&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;405 : Method Not Allowed&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;429 : Too Many Requests&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;기타 3xx 대 관련 Redirection&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;결국은 다다익선??&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[작성에 사용 된 글 들]&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://pathas.tistory.com/215&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pathas.tistory.com/215&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://sanghaklee.tistory.com/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sanghaklee.tistory.com/61&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9930695/rest-api-404-bad-uri-or-missing-resource&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/9930695/rest-api-404-bad-uri-or-missing-resource&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://laeshiny.medium.com/get-%EC%9A%94%EC%B2%AD-%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EC%97%86%EC%9C%BC%EB%A9%B4-200-or-404-4ab7430084af&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://laeshiny.medium.com/get-%EC%9A%94%EC%B2%AD-%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EC%97%86%EC%9C%BC%EB%A9%B4-200-or-404-4ab7430084af&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://cheese10yun.github.io/checked-exception/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://cheese10yun.github.io/checked-exception/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/108</guid>
      <comments>https://hankkuu.tistory.com/108#entry108comment</comments>
      <pubDate>Sun, 12 Oct 2025 21:47:02 +0900</pubDate>
    </item>
    <item>
      <title>테스트 기반 개발 방법 실행하기(도입기 2)</title>
      <link>https://hankkuu.tistory.com/106</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지난번 KCRM 웹 어플리케이션이 서비스 되면서 Rest Docs 기반의 API 명세문서를 사용하게 되었고 아래와 같은 문서로 배달의 민족의 노하우를 일부 흡수해왔습니다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://hankkuu.tistory.com/103&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;테스트 기반 개발 방법 도입기1 &lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;테스트가 뭔지는 알것 같은데 테스트로 개발하는 것은 뭐지? 이런 부분 까지는 모르는 상황에서 일단 Controller Layer 에 대해서 테스트를 진행했고 이번에는 전체적인 개발에 있어 테스트 기반으로 시작했고 그 나름의 가이드를 문서화 했습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예제로 쓰인 코드는 사내 깃랩에 오픈했고 필요하시면 권한을 획득해서 접근할 수 있습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재 스프링 부트 테스트(JUnit5) 기반의 내용으로만 되어 있어 도입기 3에는 테스트 기반 개발 방법 보충하기 가 될 것 같습니다 (3부작) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기에는 지금 담지 못한 일부 내용들이 추가될 것 같습니다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Kotlin을 위한 KoTest 사용에 대해 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Kover 로 Kotlin 기반에서 테스트 커버리지 측정하기 (vs JaCoCo)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Kotest, kover, gitlab-ci 로 테스트 자동화로 배포까지 하기 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;세세하게 모두 남길 수는 없어서 상세한 내용은 개발 가이드 문서인 컨플루언스에 남기고 아래 부분은 용어와 구현 방법 위주로 기록합니다 (한번 보시고 꼭 아래와 같은 방법이 아니어도 코드로 테스트 할 수 있는 방법이 필수가 되야 한다는 부분이 전달되었으면 합니다)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;목차는 아래와 같습니다&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트 코드란&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트 도구&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트 더블이란&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;TDD와 BDD&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트 의 종류&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Mock 에 대해&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;통합 테스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;API 테스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;서비스 테스트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;슬라이스 테스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Mock API 테스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock 서비스 테스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock Repository 테스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;외전: Mybatis 테스트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;POJO 테스트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트 커버리지와 테스트 자동화&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;JaCoCo&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Gitlab-ci&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;테스트 코드란?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;제품 or 서비스의 품질을 확인하거나 버그를 찾을 때 작성하는 코드들을 의미합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;제품이 예상대로 동작 되는지 확인할 수 있고 자동화시켜서 &lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;지속적인 품질 수준을 유지하거나 높입니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;테스트 도구는 무엇이 있을까요? (JVM 진영 기준입니다)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트 라이브러리&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;JUnit : 전통적 강자입니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;KoTest : 신흥 강자입니다. 코틀린 진영에서 많이 사용합니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Spek : 코틀린 전용입니다 - 오픈소스가 활동적이지는 않습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Spock: BDD에 특화되어 있습니다. 단 Groovy를 배워야 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock 을 위한 도구&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;MockK(Mock) : 코틀린 전용 Mockito 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;SpringMockK : MockK에서 @MockBean, @SpyBean 이 없어서 추가하게 되는 라이브러리입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;AssertJ : Assertion(단언문) 을 작성하기 위한 라이브러리로 에러나 예외처리르 담당합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;테스트 더블이란? &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어 주는 객체 입니다 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Stub, Spy, Mock 이렇게 구분은 있지만 임시 객체라는 큰 맥락에서는 같다고 보시고 쉽게 접근합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트 더블의 종류&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Dummy : 인스턴스화 된 객체는 필요하지만 기능은 필요하지 않은 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fake : 복잡한 로직에서 내부에서 필요로 하는 다른 외부 객체의 동작을 임의로 단순화 한 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Stub : 테스트에서 호출된 요청에 대해 미리 준비해둔 결과를 제공하는 경우&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Spy : Stub의 역할에 추가해서 실제 객체의 메소드를 호출까지 추가하는 경우 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Mock : 호출에 대한 기대하는 상황을 명세하고 이에 따라 동작되는 껍데기 객체를 만드는 경우&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uEogy/btsQ60AL4Qa/dWU2MzNjmSphcpaUpc6K3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uEogy/btsQ60AL4Qa/dWU2MzNjmSphcpaUpc6K3K/img.jpg&quot; data-alt=&quot;http://xunitpatterns.com/Test%20Double.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uEogy/btsQ60AL4Qa/dWU2MzNjmSphcpaUpc6K3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuEogy%2FbtsQ60AL4Qa%2FdWU2MzNjmSphcpaUpc6K3K%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;381&quot; height=&quot;129&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://xunitpatterns.com/Test%20Double.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;TDD 란? &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트를 먼저 작성하고 그 뒤에 테스트 케이스를 통과하는 코드를 작성하느 방식으로 테스트가 주도하는 개발 방법입니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;BDD 란? &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;BDD는 (Behavior Driven Development )로 TDD를 근간으로 파생된 개발 방법 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;TDD에서 한 발 더 나아가 테스트케이스 자체가 요구 사양이 되도록 하는 개발 방법 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;BDD를 통해 개발을 하게 된다면 테스트 메소드의 이름을 &quot;이 클래스가 어떤 행위를 해야한다 (should do someting)&quot; 라는 식의 문장으로 작성하여 행위에 대한 테스트에 집중할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;BDD는 시니리오를 기반으로 테스트 케이스를 작성하고 함수 단위 테스트를 권장하지 않습니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;이 시나리오는 개발자가 아닌 사람이 봐도 이해하는 레벨을 추구합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;하나의 시나리오는 Given, When, Then 구조를 가지는 것으로 기본 패턴을 권장합니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Feature : 테스트에 대상의 기능/책임을 명시합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Scenario : 테스트 목적에 대한 상황을 설명합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Given : 시나리오 진행에 필요한 값을 설정합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;When : 시나리오를 진행하는데 필요한 조건을 명시합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;간단히 생각하면 BDD 는 TDD를 어떤 방식으로 할지에 대한 구체적인 제시라고 할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;테스트의 종류는 무엇인가요? &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트 전략 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;통합 테스트&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;개발자가 변경할 수 없는 부분(외부) 까지 묶어 검증할 때 사용할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단위테스트에서 발견되지 않는 에러 부분을 확인할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;시나리오 테스트(Acceptance Test)&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;사용자 시나리오에 맞춰 수행하는 테스트(비즈니스에 초점을 둔다) 입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;누가, 어떤 목적으로, 무엇을 하는가에 대한 관점입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;개발하다 보면 API를 통해 이런 의미가 드러나고 이런 API를 시나리오에 맞게 확인하는 방식으로 이뤄집니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;명세대로 잘 동작하는지 검증하는 부분으로 블랙박스 테스트를 기본으로 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n8uSx/btsQ5TCo2FX/sp3je21C5iXKy0Cw4fYVKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n8uSx/btsQ5TCo2FX/sp3je21C5iXKy0Cw4fYVKk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n8uSx/btsQ5TCo2FX/sp3je21C5iXKy0Cw4fYVKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn8uSx%2FbtsQ5TCo2FX%2Fsp3je21C5iXKy0Cw4fYVKk%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;718&quot; height=&quot;242&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;시나리오 테스트 예시 - RestAssured&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;통합 테스트에 대해&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;모든 Bean을 올리고 테스트를 진행하기 때문에 쉽게 테스트가 가능합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;모든 Bean을 올리고 테스트를 진행하기 때문에 운영 환경과 가장 유사하게 테스트가 가능합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;API를 테스트 할 경우 요청 부터 응답까지 전체적인 테스트를 진행 가능합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;모든 Bean을 올리고 테스트를 진행하기 때문에 테스트 시간이 오래 걸립니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트의 단위가 크기 때문에 테스트 실패시 디버깅이 어려웁니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;외부 API 요청같은 Rollback 처리가 안되는 테스트를 진행할때 어렵습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;통합 테스트에 쓰이는 Annotation 종류&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;@SpringBootTest&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@AutoConfigureMockMvc&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;API 테스트 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;주로 컨트롤러 테스트를 주로 하며 요청 부터 응답까지 전체 플로우를 테스트 할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;초기 데이터베이스 셋팅이 필요한 부분에 대해 하나하나 셋팅이 되어야 합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Kotlin DSL 이 잘 되어 있긴 하지만 response 를 검증할 때 라이브러리 사용방법을 익혀야 제대로 사용할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmReFo/btsQ6jOwDQ0/LIT67hoPdaQ1G8YDgIy1Q1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmReFo/btsQ6jOwDQ0/LIT67hoPdaQ1G8YDgIy1Q1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmReFo/btsQ6jOwDQ0/LIT67hoPdaQ1G8YDgIy1Q1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmReFo%2FbtsQ6jOwDQ0%2FLIT67hoPdaQ1G8YDgIy1Q1%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;765&quot; height=&quot;556&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;@SpringBootTest 의 Base 클래스인 SpringTestSupport 를 상속받습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;통합 테스트에 필요한 기능을 protected 를 통해 제공해 줄 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;유틸성 메소드도 공통 클래스로 추가해서 테스트 코드의 편의를 높일 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fpurpleio.atlassian.net%2Fwiki%2Fspaces%2Fsp%2Fpages%2F17072232%2FAPI%23Test-Code&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;span&gt;실제 사용 예시&lt;/span&gt;&lt;/a&gt;&lt;span&gt;(컨플루언스)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;서비스 테스트&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;통합 테스트의 Base 클래스로서 일관성있는 상속으로 테스트를 처리할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;DB 까지 연결 됨에 따라 해당 환경에 의존성이 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;1004&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRYKjK/btsQ580LQUM/8HyorOV5ayBBOIo5lHMKq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRYKjK/btsQ580LQUM/8HyorOV5ayBBOIo5lHMKq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRYKjK/btsQ580LQUM/8HyorOV5ayBBOIo5lHMKq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRYKjK%2FbtsQ580LQUM%2F8HyorOV5ayBBOIo5lHMKq1%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;777&quot; height=&quot;1004&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;1004&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;@TestConstructor 를 통해 테스트 코드에서 생성자 주입이 가능해졌습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fpurpleio.atlassian.net%2Fwiki%2Fspaces%2Fsp%2Fpages%2F17039433%23Test-Code&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;span&gt;실제사용 예시&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Mock 에 대해 &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;다양한 테스트 상황을 위해서 임의이 가짜 객체를 만드는 것을 말합니다 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock의 검증 순서는 아래의 패턴을 반복합니다 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Mock 만들기 (create)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock 동작 지정 (stub)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock 의 사용 (test)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;검증 (verify)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock 사용 시 주의할 점 및 적절한 사용 방법&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;실제 환경에서는 제대로 동작하지 않을 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mock 을 사용한다면 성공을 의도하고 테스트를 작성할 수 있어 완벽한 테스트로 보기는 힘들게 됩니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;슬라이스 테스트에 대해 &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNjAVL/btsQ5FY3iCL/0xf25YyIvYo5paXe2DuqW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNjAVL/btsQ5FY3iCL/0xf25YyIvYo5paXe2DuqW0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNjAVL/btsQ5FY3iCL/0xf25YyIvYo5paXe2DuqW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNjAVL%2FbtsQ5FY3iCL%2F0xf25YyIvYo5paXe2DuqW0%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;787&quot; height=&quot;340&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사용하는 테스트와 구현 클래스&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;슬라이스 테스트의 Annotation 종류&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;@WebMvcTest&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@DataJpaTest&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@MybatisTest&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@JsonTest&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@RestClientTest&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Mock API 테스트 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;통합 테스트 보다 빠르게 테스트 할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;통합 테스트를 진행하기 어려운 테스트를 진행합니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;외부 API 같은 Rollback 처리가 힘들거나 불가능한 테스트를 주로 합니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;예를 들면 외부 결제 모듈 API를 호출해서 블랙박스를 Mock으로 치환해서 이용해야 할 때 사용할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Mock 기반으로 테스트하기 때문에 실제 환경에서는 제대로 동작하지 않을 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;679&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beQRRF/btsQ7eMNbzI/vlkaHxy5WA95bLqtmKqlA1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beQRRF/btsQ7eMNbzI/vlkaHxy5WA95bLqtmKqlA1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beQRRF/btsQ7eMNbzI/vlkaHxy5WA95bLqtmKqlA1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeQRRF%2FbtsQ7eMNbzI%2FvlkaHxy5WA95bLqtmKqlA1%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;1036&quot; height=&quot;679&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;679&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;@TestEnvironment 로 테스트 Profile 을 주입 받습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;MockMvc 의 경우는 setup 과정을 거쳐서 설정을 하고 ObjectMapper 는 @Autowire 로 의존성 주입을 받습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@RestDocsConfiguration 을 추가해서 RestDocs 기능을 추가할 수 있습니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Swagger-UI 를 쓰면서 테스트를 같이 할 수 있고 Rest-Doc 을 안 쓰면서 Mvc 테스트를 할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;API 개수가 많아진다면 Rest Docs 의 정적 페이지로는 한계가 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Rest Docs 웹 페이지를 도메인에 따라 분리하거나 Swagger-UI로 많은 API 목록을 슬라이드 동적 기능으로 처리하고 Controller Layer 의 단위 테스트를 따로 하는 것도 고려해 볼 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fpurpleio.atlassian.net%2Fwiki%2Fspaces%2Fsp%2Fpages%2F14516280%23TestCode&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;span&gt;실제사용 예시&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Mock 서비스 테스트 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;진행하고자 하는 비즈니스에만 집중해서 진행할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;중요한 관점이 아닌 것들은 Mocking 해서 외부 의존성을 줄일 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;의존성있는 객체를 Mocking 하기 때문에 온전한 테스트가 아닙니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mocking 하기가 귀찮고 이 라이브러리에 대한 선행 학습이 필요합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ye1tJ/btsQ6UHKaWa/de2d1GxpEiOoAA7aKEpx11/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ye1tJ/btsQ6UHKaWa/de2d1GxpEiOoAA7aKEpx11/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ye1tJ/btsQ6UHKaWa/de2d1GxpEiOoAA7aKEpx11/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fye1tJ%2FbtsQ6UHKaWa%2Fde2d1GxpEiOoAA7aKEpx11%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;770&quot; height=&quot;171&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;주로 Service 영역이나 Adaptor 영역을 테스트 합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Spring Layer 는 아니지만 다양한 라이브러리들의 간단한 기능 단위 테스트를 할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mocking 기반의 테스트를 합니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;코틀린에서는 Mockito 가 아닌 MockK 를 사용합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fpurpleio.atlassian.net%2Fwiki%2Fspaces%2Fsp%2Fpages%2F14516322%23TestCode&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;span&gt;실제사용 예시&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Mock Repository 테스트&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Repository 관련된 Bean 들만 등록하기 때문에 통합 테스트에 비해서 빠릅니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Repository 에 대한 관심사만 가지기 때문에 테스트 범위가 작습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트 범위가 작아서 실제 환경과 차이가 발생할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r1AR4/btsQ6Tvkfq0/6HA9WUMbijkhCdyOw8cS21/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r1AR4/btsQ6Tvkfq0/6HA9WUMbijkhCdyOw8cS21/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r1AR4/btsQ6Tvkfq0/6HA9WUMbijkhCdyOw8cS21/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr1AR4%2FbtsQ6Tvkfq0%2F6HA9WUMbijkhCdyOw8cS21%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;774&quot; height=&quot;201&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;@DataJpaTest 로 JPA 관련 Bean 만 로드 합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트가 끝날 때마다 자동으로 테스트에 사용한 데이터를 롤백합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@TestEnvironment 로 Test Profile 을 주입받습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@AutoConfigureTestDatabase 로 DB 설정을 합니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Replace.ANY 를 사용하면 기본으로 내장된 임베디드 데이터베이스를 사용합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Replace.NONE 을 사용하면 profile 에 지정된 데이터소스를 사용합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;a style=&quot;list-style-type: none; color: #000000; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fpurpleio.atlassian.net%2Fwiki%2Fspaces%2Fsp%2Fpages%2F14549102%23TestCode&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;span&gt;실제사용 예시&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;외전 : JPA 가 아닌 Mybatis 를 쓰고 있어요!! &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;CRM 의 레거시에는 JPA 는 있지 않습니다 사실 단일화 된 데이터베이스 연결 기술이라는 부분보다는 JDBC 와 관련된 다양한 라이브러리들을 기반으로 어플리케이션 개발하는 일이 더 많을 것으로 생각됩니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;실제 KCRM에서 Mybatis 단위 테스트한 코드를 예제로 담았습니다 여기서 사용한 Test Annotation은 Mock Service 로 만든 @SpringServiceTestSupport 입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Port and Adapter 패턴을 따르고 있기 때문에 SQL 이 아닌 NoSQL 연결이 생기더라도 일관성있는 테스트 행위가 필요합니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그렇기 때문에 라이브러리에 종속되는 Annotation 을 쓰기보다는 어느 Layer 에서 테스트가 이루어 지는지 구조적인 관점에서 범용성을 가져야 한다고 생각했습니다 &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QfWfl/btsQ509Hk4Z/KuatIjVVyIoXOQW9Y6u5R1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QfWfl/btsQ509Hk4Z/KuatIjVVyIoXOQW9Y6u5R1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QfWfl/btsQ509Hk4Z/KuatIjVVyIoXOQW9Y6u5R1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQfWfl%2FbtsQ509Hk4Z%2FKuatIjVVyIoXOQW9Y6u5R1%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;675&quot; height=&quot;1078&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;Mybatis 인지 JPA 인지가 중요하지 않게 되었습니다 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;검증하려는 기능에 맞게 Given - When - Then 패턴으로 확인 하면 됩니다 &lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span&gt;분량 떄문에 모든 테스트 케이스에 따른 예제를 담지는 못했는데 전반적인 내용은 위 Mybatis 와 비슷합니다&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;POJO 테스트&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;객체의 기능에 대한 테스트를 진행합니다 (Entity, DTO, VO, etc.. )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;객체지향에서 본인의 책임(기능)은 본인 스스로 제공해야 합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;JPA 기반에서는 Entity로 대표되지만 일반적인 &lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/Plain_Old_Java_Object&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;POJO&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 기반의 Test를 통칭합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Entity 대상이 테스트 될 수 있고 Utility 클래스가 대상이 될 수 있고 Domain 모델이 대상이 될 수 있습니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;장점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;외부에서 주입받을 의존성이 없어 Mocking 에 대한 대상도 없습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Entity 객체는 사용하는 계층이 많으므로 테스트의 효율성이 높습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;단점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;부분 적인 객체에 대해서만 테스트가 되기 때문에 통합적인 관점에서 비즈니스를 대표할 수는 없습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bizake/btsQ6IgvwQ6/LikBdgkk99hKcgwczRZTbk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bizake/btsQ6IgvwQ6/LikBdgkk99hKcgwczRZTbk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bizake/btsQ6IgvwQ6/LikBdgkk99hKcgwczRZTbk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbizake%2FbtsQ6IgvwQ6%2FLikBdgkk99hKcgwczRZTbk%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;783&quot; height=&quot;319&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;DTO 에서 쓰이는 Spring Validation 기능을 테스트 하기위해 Validator 를 주입 했습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OySES/btsQ5SQ1vRH/15iRdcdtOzKgEtt1QyMQr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OySES/btsQ5SQ1vRH/15iRdcdtOzKgEtt1QyMQr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OySES/btsQ5SQ1vRH/15iRdcdtOzKgEtt1QyMQr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOySES%2FbtsQ5SQ1vRH%2F15iRdcdtOzKgEtt1QyMQr1%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;751&quot; height=&quot;540&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;POJO 의 생성을 담당하는 Builder는 따로 구현 했습니다 (기본값 지정)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sAHur/btsQ67mx4mf/cy0C3CCB07Ajz6roR4X8Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sAHur/btsQ67mx4mf/cy0C3CCB07Ajz6roR4X8Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sAHur/btsQ67mx4mf/cy0C3CCB07Ajz6roR4X8Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsAHur%2FbtsQ67mx4mf%2Fcy0C3CCB07Ajz6roR4X8Gk%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;435&quot; height=&quot;612&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;테스트 커버리지와 테스트 자동화 &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;더 나은 코드를 만들기 위해서는 피드백이 필요합니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;한번에 완벽하게 해결하기를 바라는 것보다 점진적 개선에 만족하고 피드백을 통해 점점 완벽에 다가 갑니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;우아한 테크 코스 교육 방침: &lt;/span&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-10-24-code-coverage/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;코드 커버리지(Code Coverage)가 뭔가요?&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;코드 커버리지란?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span&gt;In &lt;/span&gt;&lt;/i&gt;&lt;a style=&quot;list-style-type: none; color: #216fdb; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FComputer_science&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;i&gt;&lt;span&gt;computer science&lt;/span&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;span&gt;, &lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;b&gt;&lt;span&gt;test coverage&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;i&gt;&lt;span&gt; is a measure used to describe the degree to which the &lt;/span&gt;&lt;/i&gt;&lt;a style=&quot;list-style-type: none; color: #216fdb; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FSource_code&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;i&gt;&lt;span&gt;source code&lt;/span&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;span&gt; of a &lt;/span&gt;&lt;/i&gt;&lt;a style=&quot;list-style-type: none; color: #216fdb; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FComputer_program&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;i&gt;&lt;span&gt;program&lt;/span&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;span&gt; is executed when a particular &lt;/span&gt;&lt;/i&gt;&lt;a style=&quot;list-style-type: none; color: #216fdb; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FTest_suite&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;i&gt;&lt;span&gt;test suite&lt;/span&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;span&gt; runs. A program with high test coverage, measured as a percentage, has had more of its source code executed during testing, which suggests it has a lower chance of containing undetected &lt;/span&gt;&lt;/i&gt;&lt;a style=&quot;list-style-type: none; color: #216fdb; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FSoftware_bug&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;i&gt;&lt;span&gt;software bugs&lt;/span&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;span&gt; compared to a program with low test coverage. - &lt;/span&gt;&lt;/i&gt;&lt;a style=&quot;list-style-type: none; color: #216fdb; text-align: inherit;&quot; href=&quot;https://l.workplace.com/l.php?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FCode_coverage&amp;amp;h=AT3bL6EQ07jOa1xEQfKcLhDcG8hKwYnpScy-Iff7DfJ4naaGypA57RgNSPnLH96q0wgnV8vF9x9HxAaYAdVRwrxI9thsGUgPY6PDpxf284xQEzwsNbr2cAuHrqhzRicO45f80tJWXQ-Pg1hbXRXrmQ&quot;&gt;&lt;i&gt;&lt;span&gt;wikipedia&lt;/span&gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;소프트웨어의 테스트 케이스가 얼마나 충족되었는지를 나타내는 지표 중 하나입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트를 진행했을 때 코드 자체가 얼마나 실행되었느냐는 것이고 이를 수치를 통해 확인할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;코드 커버리지의 측정 기준&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;소스 코드를 기반으로 수행하는 &lt;/span&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;화이트 박스 테스트&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;span&gt;를 통해 측정합니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;블랙 박스 테스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;소프트웨어의 내부 구조나 작동 원리를 모르는 상태에서 동작을 검사하는 방식입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;올바른 입력과 올바르지 않은 입력을 입력해서 각각 상황에 맞는 출력이 나오는지 테스트 하는 기법입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;사용자 관점의 테스트 방법이라 볼 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;화이트 박스 테스트&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;응용 프로그램의 내부 구조와 동작을 검사하는 테스트 방식입니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;소프트웨어 내부 소스 코드를 테스트 하는 기법입니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;개발자 관점의 단위 테스트 방법이라고 볼 수 있습니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;코드 커버리지가 왜 중요하죠?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코드 커버리지의 중요성은 테스트 코드의 중요성과 같습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;테스트 코드는 발생할 수 있는 모든 시나리오에 대해 작성되어야 합니다 그런데 개발자도 사람인지라 테스트로 커버하지 못하는 부분이 발생될 수 있습니다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;이럴 때 테스트에서 놓칠 수 있는 부분들을 코드 커버리지를 통해 눈으로 확인할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;코드 커버리지는 휴먼에러를 최대한 방지할 수 있도록 도와주는 용도라고 생각해도 될 것 입니다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;코드 커버리지를 활용하는 방법&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;코드 커버리지와 소나큐브와 같은 정적 코드 분석을 함께 활용해서 코드 커버리지가 기존보다 떨어지는 경우 커밋이 불가능하도록 제한할 수도 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이처럼 코드 커머비지는 코드의 안정성을 어느정도 보장 해 줄 수 있는 지표이기 때문에 테스트 코드의 중요성을 느낀다면 휴면에러를 내지 않기 위해서라도 코드 커버리지를 측정하고 발전시키려고 노력해야 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRUoYo/btsQ69q7fb5/lB7PL7ckbKcKoarRvqi5bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRUoYo/btsQ69q7fb5/lB7PL7ckbKcKoarRvqi5bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRUoYo/btsQ69q7fb5/lB7PL7ckbKcKoarRvqi5bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRUoYo%2FbtsQ69q7fb5%2FlB7PL7ckbKcKoarRvqi5bK%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;778&quot; height=&quot;567&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Test 단계에서 실패된 CI pipeline 예시 (Mauve point)&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;JaCoCo 를 통한 테스트 커버리지 측정&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MLLr8/btsQ5iC0T4c/8cksGPHKcTURFKQLGpE2c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MLLr8/btsQ5iC0T4c/8cksGPHKcTURFKQLGpE2c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MLLr8/btsQ5iC0T4c/8cksGPHKcTURFKQLGpE2c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMLLr8%2FbtsQ5iC0T4c%2F8cksGPHKcTURFKQLGpE2c0%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;2242&quot; height=&quot;602&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #65676b;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예제 프로젝트 코드 커버리지 : 82% 달성&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;테스트 코드 작성에서 끝이 아니라 테스트 자동화를 통한 빌드와 배포까지 적용할 수 있어야 어플리케이션의 품질을 보장할 수 있는 수단이 될 것 으로 보입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재 CRM 파트에서 운영하는 어플리케이션에는 테스트 코드와 코드 커버리지를 통한 CI/CD 레벨의 테스트 자동화를 도입해보고 있습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기에 코드 품질(커버리지 수치 높이기, 코드 리뷰 하기, (신규개발자, 기존 인원을 위한)개발 문서 작성 등과 같은 SW 개발의 완성도를 높이는 방법들을 도입해보려 하고 있습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아직 걸음마 단계라 많은 피드백과 제안들은 언제나 환영입니다!! (문서 및 코드) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;내용이 길어질 것 같아 깨달음에 대한 내용 보다는 인용 부분이 많습니다 추가로 참조 내역을 남깁니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;컨플루언스에는 개발 가이드로서 발전되도록 꾸준히 업데이트 할 예정입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;/span&gt;&lt;/h2&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://cheese10yun.github.io/all-tags/#Test-list&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;김남윤 개발자 - 테스트&lt;/span&gt;&lt;/a&gt;&lt;span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=L_1UYlJyNuk&amp;amp;list=PLwouWTPuIjUgZusVm_WuBVZXINH2BjP_O&amp;amp;index=11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;최범균 개발자 - 테스트&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;박재성 개발자 - 우아한 형제들 개발자 교육 담당&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/tags/test/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;참고1&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/306&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;참고2&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/106</guid>
      <comments>https://hankkuu.tistory.com/106#entry106comment</comments>
      <pubDate>Sun, 12 Oct 2025 21:42:47 +0900</pubDate>
    </item>
    <item>
      <title>테스트 기반 개발 방법 도입기 1</title>
      <link>https://hankkuu.tistory.com/103</link>
      <description>&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Kotlin Spring Boot 기반의 신규 프로젝트를 시작했습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기술 스펙은 아래와 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Spring Boot 2.6.6&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;2.7 버전으로 올릴 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Kotlin 1.6.10&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;2.0 버전으로 올릴 수 있음 or 1.6.21&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Persistence&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;JPA&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Mybatis&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Test&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;JUnit - Kotlin 기반의 &lt;/span&gt;&lt;a href=&quot;https://isntyet.github.io/kotlin/Kotest-%ED%95%B4%EB%B3%B4%EA%B8%B0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Kotest&lt;/span&gt;&lt;/a&gt;&lt;span&gt; 가 좋다고 하지만 도입하지는 않았습니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Security&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;Session - Cookie&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Clean Architecture 를 표방하기 때문에 Mauve Point 와 비슷한 구조로 진행됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다만 Legacy 부분과 격리 및 연동을 위해 가능하다면 Domain Model 을 도출해 재사용 가능한 Model 과 비즈니스 로직을 분리하려고 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Legacy 영역에서는 Mybatis 가 쓰이고 그 밖의 새로운 기능은 JPA로 구현하기로 했습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;앞으로는 테스트를 거쳐야만 빌드/배포가 가능한 SW 개발을 하기 위해 시작한 부분을 설명하고 발전시킬 부분에 대해 얘기하고자 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;먼저 선행 학습으로 본 내용으로는 최범균님이 소개해준 내용을 참고했습니다&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=L_1UYlJyNuk&amp;amp;list=PLwouWTPuIjUgZusVm_WuBVZXINH2BjP_O&amp;amp;index=11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;세미나 공유 - 테스트&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=rs_ReNmLISw&amp;amp;list=PLwouWTPuIjUgZusVm_WuBVZXINH2BjP_O&amp;amp;index=13&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;세미나 공유 - TDD 테스트 작성 순서, 기능 명세, 그리고 시연 한 번 더&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tlekdB8lAlA&amp;amp;list=PLwouWTPuIjUgZusVm_WuBVZXINH2BjP_O&amp;amp;index=15&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;세미나 공유 - TDD 테스트 코드 구조, 대역&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WBVjBwKx47I&amp;amp;list=PLwouWTPuIjUgZusVm_WuBVZXINH2BjP_O&amp;amp;index=16&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;세미나 공유 - 테스트 가능 구조&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;b&gt;&lt;span&gt;첫 번째 시도&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;API 문서를 Rest Doc 기반으로 작성하기&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2597/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Spring Rest Doc 적용&lt;/span&gt;&lt;/a&gt;&lt;span&gt; - 우아한 형제들&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2678/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Spring REST Docs에 날개를&amp;hellip; (feat: Popup)&lt;/span&gt;&lt;/a&gt;&lt;span&gt; - 우아한 형제들&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 내용을 기반으로 작성했고 Java 기반으로 되어 있기 때문에 이 부분을 Kotlin 으로 전부 변환 해봤습니다&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Java - &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/hojinDev/restdocs-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hojinDev/restdocs-sample&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Kotlin -&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Gitlab의 개인 레파지로리 이므로 내용을 보고 싶으시면 메신저로 요청주세요&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;왜? 수고스럽게 변환을 진행했냐면 기존에 사용했던 Swagger 형태에 비해 UI 가 많이 별로 입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Swagger-ui 코드를 커스텀하게 수정해서 사용한 적이 있는데 내부에 Backbone-JS 라는 것으로 동적 HTML 생성이 가능하게 되어 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;정적인 화면이 아니라 클릭 이벤트나 테스트도 가능할 정도로 입력에 있어 사용자와 Interactive 하게 만들어지는 장점이 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Rest Doc 은 정적인 HTML 화면입니다 즉 스크롤 기능도 제한적이고 슬라이드 업/다운 같은 기능도 없습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;화면 구성을 직접 구성해야 하고 공통 코드나 Request/Response 에 대해서도 표를 따로 구성해야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본 화면 Base를 따라 가기 위해 직접 우아한 형제들 오픈 프로젝트를 Kotlin 언어로 변환해서 클론 코딩을 해봤습니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;적은 시간으로 아래와 같은 Utils 와 공통 부분을 생성했습니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcIhVh/btsQ62STHV4/gS8hrbuh7NmrQUorHBztR1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcIhVh/btsQ62STHV4/gS8hrbuh7NmrQUorHBztR1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcIhVh/btsQ62STHV4/gS8hrbuh7NmrQUorHBztR1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcIhVh%2FbtsQ62STHV4%2FgS8hrbuh7NmrQUorHBztR1%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;846&quot; height=&quot;1258&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;index.html 이 산출물 파일이며 저 파일을 서버에 올려서 웹 브라우저에서 볼 수도 있습니다&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;ApiDocumentUtil : Pretty Print 설정을 하는 파일입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CustomResponseFieldsSnippet : 테스트로는 API 에 대한 데이터만 만들어지기 때문에 공통 코드에 대해 Generic 하게 처리하는 부분입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DocumentFormatGenerator : DateTime Format 을 정하는 부분으로 필요한 Format 을 추가할 수 있습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DocumentLinkGenerator : 팝업 화면을 설정할 수 있는 부분입니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;MockitoHelper : Java 버전에는 없는 부분으로 Kotlin이 기본 Final Class 속성이어서 Mocking 이 안되는 부분이 있어 추가해 넣었습니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CommonDocumentationTest, Docs, EnumViewController : 응답 코드/에러 코드 와 같은 공통 데이터를 정의한 부분입니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Swagger 로 작성된 문서와 비교하면 더 어렵고 불편한 부분이 있습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Swagger 의 장점 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;UI 가 더 예쁘게 나온다 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;문서가 편하게 자동 완성이 된다 &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Rest Docs 의 장점 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;테스트 기반 코드로 문서를 만들 수 있다 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span&gt;즉 테스트기반 코드를 만들 수 있다&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dk1DAE/btsQ4NQufL9/aKTKba1aPuSHhESEumrLpK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dk1DAE/btsQ4NQufL9/aKTKba1aPuSHhESEumrLpK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dk1DAE/btsQ4NQufL9/aKTKba1aPuSHhESEumrLpK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdk1DAE%2FbtsQ4NQufL9%2FaKTKba1aPuSHhESEumrLpK%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;1280&quot; height=&quot;318&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;테스트를 수행하면 아래와 같은 폴더에 snippet 파일이 생기고 index.adoc 파일을 직접 편집하면 파일 기반으로 index.html 이 생성됩니다 &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;1462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MImQN/btsQ4pbgikY/GUO9z7rizTd0EhSapmYz1k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MImQN/btsQ4pbgikY/GUO9z7rizTd0EhSapmYz1k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MImQN/btsQ4pbgikY/GUO9z7rizTd0EhSapmYz1k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMImQN%2FbtsQ4pbgikY%2FGUO9z7rizTd0EhSapmYz1k%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;604&quot; height=&quot;1462&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;1462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Swagger-UI 에 비교하면 아래 내용이 추가로 한땀 한땀 만들어야 하는 부분이 있어 불편하긴 합니다 &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;1233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC13Cs/btsQ6TonSGo/t9g6KnkSgFKm0D2Q4ZypU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC13Cs/btsQ6TonSGo/t9g6KnkSgFKm0D2Q4ZypU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC13Cs/btsQ6TonSGo/t9g6KnkSgFKm0D2Q4ZypU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC13Cs%2FbtsQ6TonSGo%2Ft9g6KnkSgFKm0D2Q4ZypU1%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;1220&quot; height=&quot;1233&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;1233&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #050505;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #050505;&quot;&gt;&lt;span&gt;두 번째 시도 &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;빌드 및 배포 상황에 테스트 먼저 수행하기 &lt;/span&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;628&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPk4IR/btsQ6LDSXa2/U9j4ei3edkrQcpAITkE2ck/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPk4IR/btsQ6LDSXa2/U9j4ei3edkrQcpAITkE2ck/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPk4IR/btsQ6LDSXa2/U9j4ei3edkrQcpAITkE2ck/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPk4IR%2FbtsQ6LDSXa2%2FU9j4ei3edkrQcpAITkE2ck%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;628&quot; height=&quot;1158&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8DVg9/btsQ5RdyhV6/MSW3DCQm4eEGJO9VMFrkN1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8DVg9/btsQ5RdyhV6/MSW3DCQm4eEGJO9VMFrkN1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8DVg9/btsQ5RdyhV6/MSW3DCQm4eEGJO9VMFrkN1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8DVg9%2FbtsQ5RdyhV6%2FMSW3DCQm4eEGJO9VMFrkN1%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;734&quot; height=&quot;546&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1760271089387&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tasks {
        val snippetsDir by extra { file(&quot;build/generated-snippets&quot;) }  // 변경   
       
       clean {
              delete(&quot;src/main/resources/static/docs&quot;)
       }  
        test {
               useJUnitPlatform() 
               outputs.dir(snippetsDir)
        }      
        asciidoctor {
                dependsOn(test)
                inputs.dir(snippetsDir)  
                doFirst {
                         delete(&quot;src/main/resources/static/docs&quot;)
                }      
        }
        register&amp;lt;Copy&amp;gt;(&quot;copyDocument&quot;) {
                  dependsOn(asciidoctor)  
                  from(asciidoctor.get().outputDir) {
                         into(&quot;src/main/resources/static/docs&quot;)
                  }
        }    
        build {
              dependsOn(&quot;copyDocument&quot;)   
        }  
        bootJar {
                dependsOn(&quot;copyDocument&quot;)
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;일단 테스트 코드 작성에 대한 내용 자체는 여기서 한번 끊고 가려고 합니다 (일단 인프라 만 만들기)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재 API 문서를 만들기 위한 테스트 부분은 API 요청과 응답에 대한 Controller 영역에 대한 슬라이스 테스트로 진행되는 부분으로 전체 적인 테스트를 하는 것은 아닙니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;통합 테스트 관점으로 모든 것을 해결하려하면 테스트 수행 시간이 점점 오래 걸리게 됩니다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;점점 누적되고 쌓인 테스트로 개발-검증 생산성이 높아져야 하는 부분인데 이 부분은 분리해서 진행하려고 합니다 &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UGcYh/btsQ7uBNpPX/RDht06TbtsnUUUMVLk25BK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UGcYh/btsQ7uBNpPX/RDht06TbtsnUUUMVLk25BK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UGcYh/btsQ7uBNpPX/RDht06TbtsnUUUMVLk25BK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUGcYh%2FbtsQ7uBNpPX%2FRDht06TbtsnUUUMVLk25BK%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;734&quot; height=&quot;344&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래는 추가로 공부하려고 수집한 내용입니다 &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;유료 강의 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://fastcampus.co.kr/dev_red_ygw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;이규원 - 강남언니 CTO&lt;/span&gt;&lt;/a&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;코드리뷰/리팩토링/TDD&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;책 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=233614629&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;테스트 주도 개발 시작하기&lt;/span&gt;&lt;/a&gt;&lt;span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=280870631&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Unit Testing&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;세미나 &lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=AE7K-16dEjo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;[SpringCamp2013] TDD 라이브&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=hFXkjZthuzU&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;14회 공감세미나_TDD 발담그기/ 신림프로그래머, 최범균&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=5PDYHNCcjYM&amp;amp;list=PLwouWTPuIjUg5gQBL9ajinVkcX4D8BAkI&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;코딩 연습 : TDD 연습&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aN0I0qYm28k&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;코딩 연습 : TDD 대역 연습&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #050505;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;테스트를 도입하고 설계에 응용하는 방법과 의견들이 있으면 언제든 말을 걸어 주세요 (스터디도 있습니다)&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #050505; text-align: start;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b08761/btsQ6VT9G5Q/Xsv4yzSLekGiioLAOW9ImK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b08761/btsQ6VT9G5Q/Xsv4yzSLekGiioLAOW9ImK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b08761/btsQ6VT9G5Q/Xsv4yzSLekGiioLAOW9ImK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb08761%2FbtsQ6VT9G5Q%2FXsv4yzSLekGiioLAOW9ImK%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;1440&quot; height=&quot;1236&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;</description>
      <category>My Work/Tech Blog</category>
      <author>hankkuu</author>
      <guid isPermaLink="true">https://hankkuu.tistory.com/103</guid>
      <comments>https://hankkuu.tistory.com/103#entry103comment</comments>
      <pubDate>Sun, 12 Oct 2025 21:31:46 +0900</pubDate>
    </item>
  </channel>
</rss>