<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>글쓰는 개발자</title>
    <link>https://souljit2.tistory.com/</link>
    <description>기술을 깊이 탐구하고, 코드에 녹여
높은 품질의 코드를 생산하는 것에 가치를 둡니다.</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 23:08:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발하자</managingEditor>
    <item>
      <title>Claude와 git worktree를 결합해 생산성을 2배 향상한 경험담</title>
      <link>https://souljit2.tistory.com/85</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 인식&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 함께 몇년간 개발을 하면서, 문득 비효율적인 패턴을 발견했다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;나: &quot;이 API에 validation 로직 추가해줘&quot;
AI: (코드 분석 10초, 작성 20초, 테스트 30초...)
나: (메신저 확인, 스크롤, 물 떠오기...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 일을 시키는 동안 개발자는 Idle 상태에 빠지기 십상이다. 그렇다고 아무것도 안할 수는 없으니, 멍하니 기다리거나, 잠깐 휴대폰을 확인하거나, 화장실에 다녀온다. AI가 사람 대신 일을 빠르게 처리해준다는 건 좋은데, 그 사이에 개발자가 놀고 있으면 이게 진짜 생산성 향상인지 모르겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 요즘은 프롬프트 한 줄이 수천줄의 코드를 만들어 내는 초생산성의 시대 아니던가. 비는 시간을 조금씩 모았으면 다른 피처들도 많이 진행시켜 놓을 수 있었을텐데. 화장실에 버리기에는 아까운 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 생각으로부터 나는 새로운 워크 플로우를 실험해보기 시작했고, 결과적으로 나의 생산성을 약 2배 가량 향상시켜준 성공적인 프로젝트가 되어 이곳에 공유하고자 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TL;DR&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude 서브에이전트 + Git worktree 조합으로 병렬 개발 구현&lt;/li&gt;
&lt;li&gt;rsync로 IDE 전환 문제 해결&lt;/li&gt;
&lt;li&gt;결과: 월간 커밋 +92%, 주당 이슈 처리량 2배&lt;/li&gt;
&lt;li&gt;핵심은 사전 설계 &amp;mdash; 충돌 회피가 병렬 개발의 성패를 가름&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git Worktree란&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치를 전환한다면 보통은 이런 패턴이 될 것이다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git stash # 또는 불필요한 git commit
git checkout feature-B
# IDE 작업
git checkout feature-A
git stash pop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 에디터로 코드를 보기 힘든 인간에겐 당연한 흐름일지 모르겠으나, 굳이 IDE로 예쁘게 코드를 보지 않아도 되는 AI에게 일을 시키기에는 느리기고 하고 작업 공간도 보존되지 않아서 불편하다.&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;나도 이런 식으로 매번 작업을 하다가 우연히 git worktree라는 기능을 알게 되었다. git worktree란 한마디로, 작업 디렉토리를 버전별로 copy해서 여러 디렉토리로 구성하는 방식이다. 가령, feature-A와 feature-B에 대한 worktree를 만들면 아래처럼 내가 지정해준 위치에 각 feature를 위한 디렉토리가 구성된다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;project/
├── main/              &amp;larr; develop 브랜치
└── worktrees/
    ├── feature-A/     &amp;larr; feature-A 브랜치 (독립 폴더)
    └── feature-B/     &amp;larr; feature-B 브랜치 (독립 폴더)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, checkout은 &lt;b&gt;하나의 디렉토리에서 브랜치를 바꾸는 것&lt;/b&gt;이고, worktree는 &lt;b&gt;브랜치마다 디렉토리를 따로 두는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 알고 나니 &quot;AI한테 각 폴더에서 동시에 작업시키면 되겠다&quot;는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 68px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignCenter&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;Git Checkout&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;Git Worktree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;브랜치 전환&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;파일 교체&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;폴더 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;동시 작업&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;불가능(또는 비효율)&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;디스크&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;1배&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;N배&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 시도: 직접 구현 (실패)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 MCP로 Git worktree를 제어하는 멀티 브랜치 시스템을 직접 만들어봤다. 당시엔 AI 에이전트 오케스트레이션 개념을 몰랐기에, &quot;여러 브랜치에서 동시에 작업하려면 멀티 AI 인스턴스를 직접 관리해야겠지&quot;라고 생각했다. 결과는 &lt;b&gt;대실패&lt;/b&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;'무모한 도전이었나?'라는 생각을 하던 와중에, oh-my-opencode의 후속 버전인 oh-my-claudecode를 알게 되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전환점: 서브에이전트 오케스트레이션&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oh-my-claude(OMC)에는 서브에이전트 오케스트레이션이 이미 구현되어 있었다. 내가 삽질했던 게 이미 다 구현되어 있었던 것이다. 역시.. 바퀴를 발명하기전에 먼저 파는게 없는지 찾아봤어야 했는데. 명령만 내리면 뚝딱뚝딱 구현해주는 시대다보니, 역설적으로 찾아볼 생각을 못했던 것 같다. 무튼 OMC를 이용하니 내가 생각했던 worktree 기반의 병렬 개발이 훨씬 잘 작동했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 &quot;병렬로 해줘&quot;라고만 말하면 되는 건 아니다. 실제로는 각 피처별로 상세한 설계를 먼저 토의해야 했다. 우리 팀은 사내 메신저인Dooray를 쓰는데, 여기에도 Jira 같은 티켓 시스템이 있어서 이걸 MCP로 연동해뒀다. 그래서 클로드가 티켓 내용을 직접 읽고 요구사항을 파악할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;나: &quot;쇼핑몰 기능을 3개 피처로 나눠서 병렬 개발하려고 해.
    티켓 #1234, #1235, #1236 내용 읽어보고 설계 토의하자.&quot;

AI: (MCP로 티켓 조회 후)
   &quot;#1234: 상품 목록 API - 페이지네이션 필수, 카테고리 필터...&quot;
   &quot;#1235: 장바구니 기능 - 비회원도 담기 가능해야 함...&quot;
   &quot;#1236: 주문 생성 - 토스페이먼츠 연동 필요...&quot;&lt;/code&gt;&lt;/pre&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;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계 문서 = 맥락 보존&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 worktree마다 &lt;code&gt;spec.md&lt;/code&gt;와 &lt;code&gt;implement.md&lt;/code&gt; 두 개의 문서를 꼭 작성한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;worktrees/feature-A/
├── A-spec.md           &amp;larr; 기능 명세, 테스트 기준
├── A-implement.md      &amp;larr; 구현 계획, 수정 파일 리스트
└── src/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하는 이유는, 한 세션에서 AI 에이전트와 여러 브랜치에 대한 설계를 토의하다 보면 나중에 맥락을 잊어버리는 경우가 많았기 때문이다. Context window에 한계가 있고, 긴 세션에서는 context compaction이 일어나기 때문이다. 그래서 설계 토의 결과를 스냅샷으로 문서화해둔다. 세션이 끊겨도 맥락 복구가 가능하고, 다른 브랜치 작업하다 돌아와도 &quot;spec.md 읽고 이어서 작업해줘&quot;라고만 하면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDE 전환 문제와 해결&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하니 병렬 개발은 원활히 잘 되었는데, 또 다른 문제가 나타났다. 당연하게도 에이전트를 시켜서 코드 개발을 하고 나면, 개발자가 손수 컨벤션에 어긋나거나 미처 맥락을 파악하지 못해 이상하게 구현된 코드들을 살펴보고 고치는 작업이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;worktrees/feature-A/    # AI가 여기서 작업
worktrees/feature-B/    # AI가 여기서 작업
worktrees/feature-C/    # AI가 여기서 작업&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는, 나는 Kotlin + Spring boot 기반 프로젝트를 개발하고 있는데, 각 worktree 코드를 리뷰하기 위해선 IntelliJ를 브랜치 수만큼 열어야 했다. 그 이유는, worktree가 브랜치를 점유하면 해당 브랜치로 checkout이 불가능해지기 때문이다. Git이 같은 브랜치를 두 곳에서 동시에 수정하는 걸 막기위해 일종의 lock을 걸어놓은 셈이다. (왜 worktree간 switching 기능은 제공하지 않을까 의문이다.)&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;처음엔 무식하게 여러 창을 켜보기도 하고, module을 import 해서 여러 프로젝트인 것처럼 사용해보기도 했지만, 굉장히 무거운 IntelliJ 창이 하나 늘어날 때마다 내 생산성을 실시간으로 저하시키는걸 느낄 수 있었다. 그리하여 &quot;IDE로 해결한다&quot;는 접근법은 포기했고, 또 다시 AI와의 토의를 거쳐 방식을 두 가지로 좁혔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Symlink 방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symlink(심볼릭 링크)는 파일이나 폴더를 가리키는 바로가기 같은 것이다. main project에서 worktree의 특정 피처 디렉토리에 symbolic link를 만들면, 굳이 창을 전환하지 않고도 코드가 바뀐 것처럼 관리할 수 있지 않을까? 생각했다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Intellij -&amp;gt; main-project -&amp;gt; symlink(worktrees/feature-product/)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 IDE 캐시가 symlink 변경을 제대로 감지하지 못한다는 점이었다. 링크 대상을 변경해도 이전 파일 인덱싱이 남아서 전혀 switching한다는 느낌을 받지 못했다. 이걸 해결하려면 invalidate cache로 cache를 날리고 프로젝트를 re-open 해줘야 하는데, 이럴 바엔 여러 intellij 창을 키는게 효율적이란 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Rsync 방식 (최종 선택)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 내가 원한 건 git checkout의 동작을 모사하는 것이다. checkout이 &quot;브랜치 내용을 현재 폴더에 덮어쓰는 것&quot;이니까, 그걸 rsync로 수동으로 하면 된다. 참고로, Rsync는 폴더 간 파일을 동기화하는 유닉스 명령어다. 변경된 파일만 복사하고, &lt;code&gt;--delete&lt;/code&gt; 옵션으로 원본에 없는 파일은 삭제한다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;rsync -av --delete worktrees/feature-product/ main-project/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 main-project만 IDE로 열어두고, AI는 worktree에서 작업한다. worktree의 작업이 끝나면, rsync를 통해 worktree의 내용을 main-project로 읽어온다. 이렇게 하니 git checkout만큼은 아니더라도 체감 95% 정돈 checkout 같은 느낌을 줄 수 있었고, 이 5%는 병렬 개발로 인해 늘어난 생산성에 대한 trade-off 비용으로 내기로 했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 워크플로우&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;shop-project/
├── main/                      &amp;larr; IDE는 여기만 열어둠 (뷰어 전용)
└── worktrees/
    ├── feature-product/       &amp;larr; AI가 실제 작업
    ├── feature-cart/
    └── feature-order/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 삽질을 거쳐, 현재 적용된 워크 플로우는 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;AI와 각 브랜치에 대해 사전 설계 토의(이것도 스킬로 만들어두면 편하다)&lt;/li&gt;
&lt;li&gt;AI가 각 worktree에서 작업 (독립 브랜치에서 동시 개발)&lt;/li&gt;
&lt;li&gt;코드 리뷰: &lt;code&gt;rsync -av --delete worktrees/feature-cart/ main/&lt;/code&gt;(보통 zsh 명령어로 대체해서 사용)&lt;/li&gt;
&lt;li&gt;각 브랜치별 PR 생성, 리뷰 완료된 것부터 순차 병합&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌 회피 전략&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 작성하지 않은 내용이 있는데, 충돌 관리는 병렬 개발에서 어쩌면 가장 신경써야 하는 부분일지 모른다. 무작정 병렬로 돌리면 merge hell을 경험하게 될 지도 모르니까. 해결책은 사전 설계 투자다. 물론 이것도 내가 할 일은 아니고, AI에게 자연어로 열심히 시키면 된다. 그러기 위해 사전 설계를 다음과 같이 구성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1770212101343&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Phase 1: 전체 피처 설계]
- 병렬 개발할 피처 A~E에 대한 개별 요구사항 정리
- 각 피처별 구현 명세 작성 (spec.md, implement.md)
- 테스트 기준 문서화
- 수정/추가될 파일 리스트 도출

[Phase 2: 충돌 분석]
- 공통 수정 파일 확인
- 충돌 위험도 평가

[Phase 3: 실행 계획]
- 독립적인 피처 &amp;rarr; 병렬 진행
- 의존성 있는 피처 &amp;rarr; 순차 처리
- 공통 모듈 변경 &amp;rarr; 별도 PR 먼저 병합&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쇼핑몰 MVP에서 5개 피처를 구현한다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1770211963853&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[피처 목록]
1. 상품 목록/상세
2. 장바구니
3. 주문/결제
4. 리뷰 작성
5. 마이페이지

[충돌 분석]
Product 엔티티  &amp;rarr;  피처 1, 4에서 수정
User 엔티티     &amp;rarr;  피처 3, 5에서 수정
장바구니        &amp;rarr;  주문에 의존성 있음

[실행 계획]
Sprint 1 (병렬): product, cart
Sprint 2 (순차): order (cart 머지 후), review (product 머지 후)
Sprint 3:       mypage (order 머지 후)&lt;/code&gt;&lt;/pre&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;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정량적 결과&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스택&lt;/b&gt;: Kotlin + React&lt;br /&gt;&lt;b&gt;기간&lt;/b&gt;: 2025년 11월 ~ 2026년 1월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대상&lt;/b&gt;: 담당 개발 중인 서비스&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 95px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignCenter&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;지표&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Before(11-12월)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;After(1월)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;변화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;월간 커밋 수&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;평균 50개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;96개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;+92%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;주당 티켓 처리&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;2~3개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;4~6개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;~2배&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;피크 주간 커밋&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;21개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;43개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;2배&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;동시 활성 브랜치&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;1~2개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;6개&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;3배&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커밋이 많다고 생산성이 높은 건 아니다&lt;/b&gt;라는 반론이 있을 수 있다. 그래서 티켓 처리량을 함께 보았다. 주당 2~3개에서 4~6개로 늘었고, 동시 활성 브랜치도 눈에 띄게 증가했다. 물론 그럼에도 일감의 성격이나 당시 상황에 따라 처리 속도가 다를 수도 있겠지만, 정성적인 평가를 내려보더라도 확실히 업무 중간 유휴 시간이 줄었고, 컨텍스트 관리만 잘 해준다면 기존보다 훨씬 많은 일을 빠르게 처리할 수 있다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식이 손에 익으면, 브랜치를 빠르게 스위칭하면서 정확성 또한 놓치지 않고 여러 일감을 병렬 개발할 수 있는 능력 또한 향상하리라 생각한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느낀 점&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;좋았던 점&lt;/b&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;b&gt;작업 리듬이 붙는다&lt;/b&gt;: 하나의 피처를 기다리지 않고 계속 다음 작업으로 이동. 유휴 시간이 거의 사라진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;피처 간 연관성이 보인다&lt;/b&gt;: 여러 피처를 한 세션에서 다루다 보니 전체 그림이 보인다. &quot;이 기능은 저 피처가 끝나고 해야되겠는데?&quot; 같은 통찰이 생긴다. AI도 알게되는건 덤.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;업무 텐션이 올라간다&lt;/b&gt;: 피처 1~2개만 개발할 때는 중간에 집중력이 저하되는데, 여러 피처 돌아가며 개발하면 계속 새로운 문제에 집중하게 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의할 점&lt;/b&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;b&gt;AI 코드 검증 책임은 여전히 개발자에게&lt;/b&gt;: 병렬 개발은 리뷰할 코드량 증가를 의미한다. 병렬로 개발하더라도 리뷰 및 테스트는 여전히 직렬로 한땀한땀 진행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;충돌 관리 필수&lt;/b&gt;: 사전 설계 없이 무작정 병렬 진행하면 merge hell이 발생할테니, 시간 절약보다 충돌 해결에 더 많은 시간이 소모될 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 작업이 병렬 가능한 건 아니다&lt;/b&gt;: 복잡한 리팩토링은 순차 처리가 낫고, 탐색적 개발은 한 가지에 집중해야 할 때도 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 환경이 꼭 필요할까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기부터는 주제에서 벗어나는 얘기지만, 기록을 위해 남긴다. 이런 방식으로 개발하다 보니 의문이 생겼다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 에이전트가 실제 코딩을 하고&lt;/li&gt;
&lt;li&gt;IDE는 그저 코드 뷰어 역할이고&lt;/li&gt;
&lt;li&gt;rsync로 원격 동기화도 가능하다면&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리모트 서버에 worktree들을 두고, 거기서 AI 에이전트가 작업하게 하면 어떨까.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;[Local]                    [Remote Server]
IDE (뷰어)  &amp;lt;-- rsync --&amp;gt;  worktrees/feature-A (AI Agent 1)
                           worktrees/feature-B (AI Agent 2)
                           worktrees/feature-C (AI Agent 3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 머신 리소스 절약도 가능하고, 에이전트를 PC on/off와 관계없이 24시간 어디서든 돌릴 수 있고, 어쩌면 여러 개발자가 같은 에이전트 풀을 공유할 가능성도 생긴다. 물론 네트워크 지연, 보안, 비용, 개발자별 환경 관리 및 충돌 등 고려할 게 많지만, 상황이 허락한다면 실험해볼 만한 주제이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 시대의 개발자 역할이 바뀌고 있다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;예전: 개발자 &amp;rarr; 직접 코딩 &amp;rarr; 커밋
지금: 개발자 &amp;rarr; 설계 &amp;amp; 지휘 &amp;rarr; AI들이 병렬 코딩 &amp;rarr; 검증 &amp;amp; 병합&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 오케스트라 지휘자처럼 전체 아키텍처를 설계하고, 각 AI 에이전트에게 파트를 할당하며, 조화롭게 연주되는지 검증하는 역할이다. Git worktree + AI 서브에이전트 조합은 이런 오케스트레이션을 가능하게 만드는 인프라가 되어준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전 팁&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작게 시작하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 6개 브랜치 병렬은 무리일지 모른다. 독립적인 2개 피처로 시작해서 충돌 경험을 쌓은 후 확장하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자동화 투자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;worktree 스위칭은 rsync 기반으로 파일만 훔쳐오는 방식이기 때문에, git의 사용성만큼 끌어올리기 위해선 몇가지 명령어 예약을 해두면 좋다. 아래는 반드시 추가하면 좋을 명령어들.&lt;/p&gt;
&lt;pre id=&quot;code_1770215825287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- worktree -&amp;gt; main directory로 rsync하기 (checkout 역할)
- main directory -&amp;gt; worktree로 rsync하기 (checkout후 파일 수정하여 worktree에 재반영)
- 현재 어떤 worktree로 switching 했는지 확인하기 (파일만 가져간거라 git은 모르기 때문에 시스템 어딘가에 기록해두고 사용해야 함)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;/blockquote&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/Yeachan-Heo/oh-my-claudecode&quot;&gt;oh-my-claudecode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-worktree&quot;&gt;Git Worktree 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;(추가)&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;글을 쓰고 며칠이 지나 클로드 코드 팀이 추천하는 실전 팁이라는 글에 worktree를 활용해 병렬 세션으로 개발하는게 최고의 생산성 향상 팁이라는 글이 올라왔다. 야호! 물론 나는 단일 세션 - 병렬 서브 에이전트를 사용한다는 점에서 좀 달랐지만... 그래도 큰 방향은 일치하는 것 같아 기분이 좋았다. 심지어 &lt;b&gt;worktree에 이름을 붙이고 shell alias(za, zb, zc)를 설정해 한 번의 키 입력으로 전환하는 방식도 활용&lt;/b&gt;한다고 하니, 사실상 내 방식이 검증된 것이나 다름없지 않을까. 좀 더 업무 방식이 정형화되면, 팀이나 사내에도 공유해봐야겠다.&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://news.hada.io/topic?id=26330&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://news.hada.io/topic?id=26330&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770214827707&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Claude Code 창시자가 공개한 실전 사용 팁 | GeekNews&quot; data-og-description=&quot;1. 병렬 작업을 최대한 활용3~5개의 git worktree를 동시에 실행하고 각각 별도의 Claude 세션을 병렬로 운영하는 것이 클로드 코드 팀 내부에서 최고의 생산성 향상 팁일부는 git checkout 방식을 선호하&quot; data-og-host=&quot;news.hada.io&quot; data-og-source-url=&quot;https://news.hada.io/topic?id=26330&quot; data-og-url=&quot;https://news.hada.io/topic?id=26330&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uthlT/dJMb8RRNgBx/QfSt73sHXKiJEvNWupZwHK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bk6zM9/dJMb8SpDhlA/KnkCsQZh4yYCXJXEDBzZiK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://news.hada.io/topic?id=26330&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://news.hada.io/topic?id=26330&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uthlT/dJMb8RRNgBx/QfSt73sHXKiJEvNWupZwHK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bk6zM9/dJMb8SpDhlA/KnkCsQZh4yYCXJXEDBzZiK/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;Claude Code 창시자가 공개한 실전 사용 팁 | GeekNews&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 병렬 작업을 최대한 활용3~5개의 git worktree를 동시에 실행하고 각각 별도의 Claude 세션을 병렬로 운영하는 것이 클로드 코드 팀 내부에서 최고의 생산성 향상 팁일부는 git checkout 방식을 선호하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;news.hada.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/기타</category>
      <category>AI parallel develop</category>
      <category>claude code</category>
      <category>oh-my-claudecode</category>
      <category>OMC</category>
      <category>병렬 피처 개발</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/85</guid>
      <comments>https://souljit2.tistory.com/85#entry85comment</comments>
      <pubDate>Mon, 2 Feb 2026 22:52:10 +0900</pubDate>
    </item>
    <item>
      <title>[RxJava] Single, Maybe, Completable</title>
      <link>https://souljit2.tistory.com/81</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;Single&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 1건만 통지하거나 에러를 통지한다.&lt;/li&gt;
&lt;li&gt;데이터 통지 자체가 완료를 의미하기 때문에, &lt;b&gt;완료 통지는 하지 않는다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;데이터를 1건만 통지하므로, 데이터 개수를 요청할 필요가 없다.&lt;/li&gt;
&lt;li&gt;onNext(), onComplete()가 없으며 이 둘을 합한 onSuccess()를 제공한다.&lt;/li&gt;
&lt;li&gt;Single의 대표적인 소비자는 SingleObserver이다.&lt;/li&gt;
&lt;li&gt;클라이언트 요청에 대응하는 서버의 응답이 Single을 사용하기 좋은 대표적인 예이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;Maybe&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 1건만 통지하거나 1건도 통지하지 않고 완료 또는 에러를 통지한다.&lt;/li&gt;
&lt;li&gt;데이터 통지 자체가 완료를 의미하기 때문에 완료 통지는 하지 않는다.&lt;/li&gt;
&lt;li&gt;단, 데이터를 1건도 통지하지 않고 처리가 종료될 경우에는 완료 통지를 한다.&lt;/li&gt;
&lt;li&gt;Maybe의 대표적인 소비자는 MaybeObserver이다.&lt;/li&gt;
&lt;li&gt;데이터를 통지할 수도(just) 있고, 안할수도(empty) 있는 Optional 개념으로 보임.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;Completable&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 생산자이지만, 데이터를 1건도 통지하지 않고 완료 또는 에러를 통지한다.&lt;/li&gt;
&lt;li&gt;데이터 통지의 역할 대신에, &lt;b&gt;Completable 내에서 특정 작업을 수행한 후 해당 처리가 끝났음을&lt;/b&gt; 통지하는 역할을 한다.&lt;/li&gt;
&lt;li&gt;Completable의 대표적인 소비자는 CompletableObserver이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;참고:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1664185117589&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;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dvcH9a/hyPVqHjNgn/m9RK22TVOxKVHqcWdBUBU0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/wK7cx/hyPVq8pbC5/2cLrqOoAZ0OJb7xAMEE6v0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/mG2Np/hyPVpuTyBH/sAx1A7ddNME9q9k52OET00/img.png?width=927&amp;amp;height=517&amp;amp;face=0_0_927_517&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dvcH9a/hyPVqHjNgn/m9RK22TVOxKVHqcWdBUBU0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/wK7cx/hyPVq8pbC5/2cLrqOoAZ0OJb7xAMEE6v0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/mG2Np/hyPVpuTyBH/sAx1A7ddNME9q9k52OET00/img.png?width=927&amp;amp;height=517&amp;amp;face=0_0_927_517');&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;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Java</category>
      <category>Completable</category>
      <category>maybe</category>
      <category>RxJava</category>
      <category>Single</category>
      <category>인프런</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/81</guid>
      <comments>https://souljit2.tistory.com/81#entry81comment</comments>
      <pubDate>Mon, 26 Sep 2022 19:04:49 +0900</pubDate>
    </item>
    <item>
      <title>[RxJava] Flowable과 Observable</title>
      <link>https://souljit2.tistory.com/79</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flowable과 Observable의 비교&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Flowable은 Reactive Streams의 인터페이스를 구현하고, Observable은 그렇지 않다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 Flowable&amp;lt;T&amp;gt;는 Publisher&amp;lt;T&amp;gt;를 구현하고 있다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;/* Flowable.java */
public abstract class Flowable&amp;lt;T&amp;gt; implements Publisher&amp;lt;T&amp;gt; {
	...
}

/* Publisher.java */
package org.reactivestreams; // reactive streams 패키지

public interface Publisher&amp;lt;T&amp;gt; {

    public void subscribe(Subscriber&amp;lt;? super T&amp;gt; s);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 아래의 Observable&amp;lt;T&amp;gt;는 ObservableSource&amp;lt;T&amp;gt;를 구현하고 있다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package io.reactivex; // reactivex 패키지
...

public abstract class Observable&amp;lt;T&amp;gt; implements ObservableSource&amp;lt;T&amp;gt; {
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Floable은 배압(Back pressure) 기능을 지원하고, Observable은 지원하지 않는다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배압이란, Flowable에서 데이터를 통지하는 속도가 Subscriber에서 통지된 데이터를 전달받아 처리하는 속도보다 빠를 때, 밸런스를 맞추기 위해 데이터 통지량을 제어하는 기능을 말한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qAiCJ/btrLhXwQQoB/g6j9xdQR3yMEziwKCi4rsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qAiCJ/btrLhXwQQoB/g6j9xdQR3yMEziwKCi4rsk/img.png&quot; data-alt=&quot;Flowable 통지 속도 &amp;amp;gt; Subscriber 처리 속도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qAiCJ/btrLhXwQQoB/g6j9xdQR3yMEziwKCi4rsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqAiCJ%2FbtrLhXwQQoB%2Fg6j9xdQR3yMEziwKCi4rsk%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;258&quot; height=&quot;349&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Flowable 통지 속도 &amp;gt; Subscriber 처리 속도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, Flowable이 데이터 100개를 전달하는 동안 Subscriber는 데이터 1개를 처리한다면 어떤 일이 발생할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드를 실행해보자.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class MissingBackpressureExceptionExample {

    public static void main(String[] args) throws InterruptedException {
        Flowable.interval(1L, TimeUnit.MILLISECONDS)
            .doOnNext(data -&amp;gt; System.out.println(Thread.currentThread().getName() + &quot; - 데이터 통지 : &quot; + data))
            .observeOn(Schedulers.computation())
            .subscribe(
                data -&amp;gt; {
                    System.out.println(&quot;# 소비자 처리 대기중..&quot;);
                    Thread.sleep(1000L);
                    System.out.println(Thread.currentThread().getName() + &quot; - 데이터 처리 : &quot; + data);
                },
                err -&amp;gt; System.err.println(err),
                () -&amp;gt; System.out.println(&quot;complete&quot;)
            );

        Thread.sleep(2000L);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DUvtA/btrLihWaMX9/inwf9Cy71iJNGi3NzkYm0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DUvtA/btrLihWaMX9/inwf9Cy71iJNGi3NzkYm0k/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DUvtA/btrLihWaMX9/inwf9Cy71iJNGi3NzkYm0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDUvtA%2FbtrLihWaMX9%2Finwf9Cy71iJNGi3NzkYm0k%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;836&quot; height=&quot;533&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxComputationThreadPool-1에서 데이터를 127까지 통지했지만, RxComputationThreadPool-2에서는 데이터를 하나밖에 처리하지 못하고, 이후 &lt;b&gt;MissingBackpressureException&lt;/b&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;MissingBackpressureException이 발생하는 이유가 무엇일까? 해답은 Flowable의 배압 전략(Backpressure Strategy)에서 찾을 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배압 전략(Backpressure Strategy)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxJava에서는 배압 전략을 통해, Flowable이 통지 대기 중인 데이터를 어떻게 다룰 지에 대한 스펙을 제공한다. 배압 전략에는 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Missing 전략
&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;나중에 onBackpressureXXX()로 배압을 적용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Error 전략
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;통지된 데이터가 버퍼의 크기를 초과하면 &lt;b&gt;MissingBackpressureException &lt;/b&gt;에러를 통지한다.&lt;/li&gt;
&lt;li&gt;소비자가 생산자의 통지 속도를 따라잡지 못할 때 발생한다.&lt;/li&gt;
&lt;li&gt;즉, 위의 코드에서 MissingBackpressureException이 발생한 것은 Flowable의 기본 배압 전략이 Error 전략임을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Buffer 전략 - buffer 전략에는 2가지 전략이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DROP_LATEST
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버퍼가 가득 찬 시점에, 버퍼 내에서 가장 &lt;b&gt;최근에&lt;/b&gt; 버퍼로 들어온 데이터를 DROP&lt;/li&gt;
&lt;li&gt;DROP된 빈 자리에 버퍼 밖에서 대기하던 데이터를 채운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DROP_OLDEST
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버퍼가 가득 찬 시점에, 버퍼 내에서 가장&lt;span&gt;&amp;nbsp;&lt;b&gt;먼저&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;버퍼로 들어온 데이터를 DROP&lt;/li&gt;
&lt;li&gt;DROP된 빈 자리에 버퍼 밖에서 대기하던 데이터를 채운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DROP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버퍼가 가득 찬 시점에, 버퍼에 들어오려는 모든 데이터를 버림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LATEST&lt;/li&gt;
&lt;li&gt;OLDEST&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DROP_LATEST&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 DROP_LATEST Backpressure Strategy를 사용하는 코드이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static void main(String[] args) throws InterruptedException {
    System.out.println(&quot;# start: &quot; + Thread.currentThread().getName());
    Flowable.interval(300L, TimeUnit.MILLISECONDS)
        .doOnNext(data -&amp;gt; System.out.println(&quot;# publish : &quot; + data))
        .onBackpressureBuffer(
            2, // capacity == 2
            () -&amp;gt; System.out.println(&quot;overflow!&quot;),
            BackpressureOverflowStrategy.DROP_LATEST
        )
        .doOnNext(data -&amp;gt; System.out.println(&quot;# buffer publish : &quot; + data))
        .observeOn(Schedulers.computation(), false, 1) // subscriber가 데이터를 1개씩 요청
        .subscribe(
            data -&amp;gt; {
                Thread.sleep(1000L);
                System.out.println(&quot;subscribe : &quot; + data);
            },
            err -&amp;gt; System.out.println(&quot;error : &quot; + err)
        );

    Thread.sleep(2800);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caCB6Q/btrLg8Z7MQg/qkZeYVeDjjlrs86IDN4z9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caCB6Q/btrLg8Z7MQg/qkZeYVeDjjlrs86IDN4z9k/img.png&quot; data-alt=&quot;DROP_LATEST 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caCB6Q/btrLg8Z7MQg/qkZeYVeDjjlrs86IDN4z9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaCB6Q%2FbtrLg8Z7MQg%2FqkZeYVeDjjlrs86IDN4z9k%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;606&quot; height=&quot;456&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DROP_LATEST 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DROP_LATEST 전략은 Buffer 전략의 하위 전략이기 때문에, buffer에서도 publish라는 개념이 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FvjHr/btrLg7tlLhw/9SZh2w8PZVDVncPdIZYwxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FvjHr/btrLg7tlLhw/9SZh2w8PZVDVncPdIZYwxK/img.png&quot; data-alt=&quot; buffer publish&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FvjHr/btrLg7tlLhw/9SZh2w8PZVDVncPdIZYwxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFvjHr%2FbtrLg7tlLhw%2F9SZh2w8PZVDVncPdIZYwxK%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;247&quot; height=&quot;190&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; buffer publish&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Buffer의 capacity가 2이고, subscriber가 데이터를 처리할 때마다 buffer에서 subscriber에게 데이터를 publish하는 것을 볼 수 있다. 또한, Flowable에서 Buffer에 데이터를 전송할 때, capacity를 넘어서면 overflow가 발생하면서 buffer의 최신 데이터가 pop되어 사라지는 것을 확인할 수 있다. 즉, 아래와 같은 그림으로 나타낼 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Flowable Observable.png&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FOLln/btrLhYJB3X5/jTAM4hrf6jGKa8MWE17XA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FOLln/btrLhYJB3X5/jTAM4hrf6jGKa8MWE17XA0/img.png&quot; data-alt=&quot;DROP_LATEST &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FOLln/btrLhYJB3X5/jTAM4hrf6jGKa8MWE17XA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFOLln%2FbtrLhYJB3X5%2FjTAM4hrf6jGKa8MWE17XA0%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;772&quot; height=&quot;433&quot; data-filename=&quot;Flowable Observable.png&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DROP_LATEST &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DROP_OLDEST는 위 그림과 대부분의 과정이 비슷하고, 버퍼가 가득 찼을 때 DROP되는 아이템이 더 먼저 들어온 녀석이라는 차이만을 가진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DROP&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 DROP Backpressure Strategy를 사용하는 코드이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static void main(String[] args) throws InterruptedException {
    System.out.println(&quot;# start: &quot; + Thread.currentThread().getName());
    Flowable.interval(300L, TimeUnit.MILLISECONDS)
        .doOnNext(data -&amp;gt; System.out.println(&quot;# publish : &quot; + data))
        .onBackpressureDrop(data -&amp;gt; System.out.println(&quot;drop : &quot; + data))
        .observeOn(Schedulers.computation(), false, 1) // buffer size를 1로 지정
        .subscribe(
            data -&amp;gt; {
                Thread.sleep(1000L);
                System.out.println(&quot;subscribe : &quot; + data);
            },
            err -&amp;gt; System.out.println(&quot;error : &quot; + err)
        );

    Thread.sleep(5500L);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;707&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ms7VU/btrLkwyC3Ui/8DiAxkrsl8j2Z2uowVsVw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ms7VU/btrLkwyC3Ui/8DiAxkrsl8j2Z2uowVsVw0/img.png&quot; data-alt=&quot;DROP 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ms7VU/btrLkwyC3Ui/8DiAxkrsl8j2Z2uowVsVw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fms7VU%2FbtrLkwyC3Ui%2F8DiAxkrsl8j2Z2uowVsVw0%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;696&quot; height=&quot;707&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;707&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DROP 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DROP 전략의 경우, onBackPressureBuffer가 아닌 onBackPressureDrop 메서드를 Flowable에 체이닝하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;참고:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1664185100486&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;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dvcH9a/hyPVqHjNgn/m9RK22TVOxKVHqcWdBUBU0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/wK7cx/hyPVq8pbC5/2cLrqOoAZ0OJb7xAMEE6v0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/mG2Np/hyPVpuTyBH/sAx1A7ddNME9q9k52OET00/img.png?width=927&amp;amp;height=517&amp;amp;face=0_0_927_517&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dvcH9a/hyPVqHjNgn/m9RK22TVOxKVHqcWdBUBU0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/wK7cx/hyPVq8pbC5/2cLrqOoAZ0OJb7xAMEE6v0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/mG2Np/hyPVpuTyBH/sAx1A7ddNME9q9k52OET00/img.png?width=927&amp;amp;height=517&amp;amp;face=0_0_927_517');&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;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Java</category>
      <category>backpressure</category>
      <category>Cold Publisher</category>
      <category>Flowable</category>
      <category>Hot Publisher</category>
      <category>Observable</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/79</guid>
      <comments>https://souljit2.tistory.com/79#entry79comment</comments>
      <pubDate>Sun, 25 Sep 2022 20:46:41 +0900</pubDate>
    </item>
    <item>
      <title>[후기] 디프만 12기 백엔드 지원 후기</title>
      <link>https://souljit2.tistory.com/80</link>
      <description>&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;디자이너와 프로그래머가 만났을 때, 디프만 12기 지원 후기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;2021년 12월, NHN에 입사하여 지인들께 축하받던 때가 엊그제 같은데&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;벌써 1년차가 3개월도 채 남지 않았습니다.(나이도 한 살 더... ㅜㅜ)&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;한동안 신입 교육받으랴, 팀에도 적응하랴 여기저기 바빴지만&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이제는 어느정도 적응도 했고, 또 다른 성장의 길을 탐색하고 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;때마침 지인을 통해서 알고 있던 디프만 동아리의 12기 모집이 시작되었고&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;시기도 지금이 적기인 것 같아 &lt;b&gt;백엔드 포지션&lt;/b&gt;으로 지원하여&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;오늘(9월 22일)&amp;nbsp;&lt;b&gt;최종 합격&lt;/b&gt; 메일을 받았습니다 :)&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; 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;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;목차&lt;/h4&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;#1&quot;&gt;디프만?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot;&gt;지원 동기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot;&gt;서류 전형(1차)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4&quot;&gt;면접 전형(2차)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;1&quot; data-ke-size=&quot;size23&quot;&gt;디프만?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;943&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/badDMb/btrMHJebLDx/D9rJfpvQMWekukm02i1LV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/badDMb/btrMHJebLDx/D9rJfpvQMWekukm02i1LV0/img.png&quot; data-alt=&quot;Depromeet!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/badDMb/btrMHJebLDx/D9rJfpvQMWekukm02i1LV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbadDMb%2FbtrMHJebLDx%2FD9rJfpvQMWekukm02i1LV0%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;794&quot; height=&quot;394&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Depromeet!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.depromeet.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;디프만&lt;/a&gt;은 &lt;i&gt;&lt;b&gt;디&lt;/b&gt;자이너와 &lt;b&gt;프&lt;/b&gt;로그래머가 &lt;b&gt;만&lt;/b&gt;났을 때&lt;/i&gt;의 약자로, 말 그대로 디자이너 - 프로그래머들이 만나 IT 서비스를 기획하고 개발하여 시중에 출시하고 운영까지 해보는 경험을 갖는 동아리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/depromeet/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 레포&lt;/a&gt;를 운영하고 있어 코드를 살펴보니 퀄리티도 괜찮아 보이고, &lt;a href=&quot;https://www.depromeet.com/project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;출시한 프로젝트&lt;/a&gt;도 좋은 제품들이 많아 보여 충분히 도전해볼 만한 가치가 있는 동아리라고 생각합니다.&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;2&quot; 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;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;012&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 평균 7~8시간 업무에 몰두하고 나서, 또 개발 공부를 하려니 머리도 아프고 체력적으로 지치는 부분이 많더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 업무를 마치고 아무것도 하지 않고 놀기만 하다 보니 시간은 훌쩍 가버리고, 멈춰있는 것만 같은 제 모습에 위기의식(?)을 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 좀 으쌰으쌰해서 대학생 때처럼 열정 있는 삶을 살아보자! 하고 디프만에 지원하게 되었습니다.&lt;/p&gt;
&lt;h3 id=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;서류 전형(1차)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디프만의 1차 지원 과정은 서류 전형부터 시작합니다. 생각했던 것보다 문항 수도 많고, 고민해야 하는 내용이 꽤 많았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 있는 지원 동기부터, 성공/실패 경험, 사용하는 언어에 대한 경험 등 기업 채용의 축소판 같은 느낌이 있어 미리 고민해본다면 취업 과정에서도 도움이 될만한 문항들이 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 지원서를 쓸 때에 며칠씩 나눠 작성하는 편이라 시간이 조금 오래 걸리는 편이라, 모집 기간 내내(약 10일) 지원서를 작성했던 것 같네요. 아래는 제가 작성한 &lt;b&gt;자기소개 및 지원동기&lt;/b&gt; 문항입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xSk5/btrMULCL7FL/RRbTvQE6ptKMZYQY6SqAS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xSk5/btrMULCL7FL/RRbTvQE6ptKMZYQY6SqAS0/img.png&quot; data-alt=&quot;자기소개서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xSk5/btrMULCL7FL/RRbTvQE6ptKMZYQY6SqAS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xSk5%2FbtrMULCL7FL%2FRRbTvQE6ptKMZYQY6SqAS0%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;610&quot; height=&quot;340&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자기소개서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문항마다 편차가 있겠으나, 위 자기소개서와 같은 문항을 약 7~8개 정도 작성한다고 보시면 됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열심히 한 보람은 있었는지, 다행히 합격 메일을 받았습니다! 합격 메일은 언제나 기분이 좋네요 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;271&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wP3zC/btrMUxrjkK2/aGUFO00Pa95k7WcNVEukNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wP3zC/btrMUxrjkK2/aGUFO00Pa95k7WcNVEukNK/img.png&quot; data-alt=&quot;서류 전형 합격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wP3zC/btrMUxrjkK2/aGUFO00Pa95k7WcNVEukNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwP3zC%2FbtrMUxrjkK2%2FaGUFO00Pa95k7WcNVEukNK%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;507&quot; height=&quot;271&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서류 전형 합격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;면접 전형(2차)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접 전형은 서류 전형 합격 메일에 안내된 게더타운에서 진행되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지인분께 듣기론 기술 면접이 거의 없고, 있어도 크게 어렵지는 않을 거라고 해서 아무런 준비를 하지 않고 무덤덤하게 갔는데, 입장하자마자 스프링 코어 관련 질문을 주셔서 살짝 당황했던 기억이 나네요. 보통 스프링 기술 면접을 준비한다면 당연히 나올만한 부분이고 기본적인 질문인데, &lt;b&gt;하필!&lt;/b&gt; 한 번도 살펴본 적 없는 주제가 나와서 그냥 잘 모르겠다고 답변드렸습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 질문 이후에도 몇 가지 기본적인 CS나 운영하는 블로그 글에 대한 질문들부터 인성 면접 질문들도 몇 가지 주셨고, 다행히 이후 질문들은 적당히 잘 대답을 한 것 같네요. 개인적으로 면접은 하루 전날에 조금이라도 입을 달궈놓는(?) 연습을 해두고 들어가는 것이 당황하지 않고 면접에 임할 수 있는 준비법인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 면접관님께서 면접이 끝나기 전에 성격이 좋아보인다는 말씀을 해주셔서 조금 버벅거렸지만 붙지 않을까 생각했고, 다행히 감사하게도 합격 메일을 받았습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4FNGb/btrMUTOhirN/me9EYHCNKrjdBKK7mJe9S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4FNGb/btrMUTOhirN/me9EYHCNKrjdBKK7mJe9S0/img.png&quot; data-alt=&quot;Yeah!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4FNGb/btrMUTOhirN/me9EYHCNKrjdBKK7mJe9S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4FNGb%2FbtrMUTOhirN%2Fme9EYHCNKrjdBKK7mJe9S0%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;537&quot; height=&quot;224&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Yeah!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 5개월간 적극적으로 서비스 개발에 참여하면서, 좋은 경험과 성장을 공유하는 글도 작성해보겠습니다. :)&lt;/p&gt;</description>
      <category>대외활동/디프만</category>
      <category>Depromeet</category>
      <category>디프만</category>
      <category>백엔드</category>
      <category>지원후기</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/80</guid>
      <comments>https://souljit2.tistory.com/80#entry80comment</comments>
      <pubDate>Sun, 25 Sep 2022 20:16:54 +0900</pubDate>
    </item>
    <item>
      <title>[RxJava] Reactive Streams</title>
      <link>https://souljit2.tistory.com/78</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;목차&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#1&quot;&gt;Reactive Streams?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot;&gt;Cold Publisher &amp;amp; Hot Publisher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot;&gt;Cold Publisher &amp;amp; Hot Publisher 예제로 살펴보기&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;1&quot; data-ke-size=&quot;size23&quot;&gt;Reactive Streams?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리액티브&amp;nbsp;프로그래밍 라이브러리의 표준 사양(&lt;a href=&quot;https://github.com/reactive-streams/reactive-streams-jvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/reactive-streams/reactive-streams-jvm&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;리액티브 프로그래밍에 대한 인터페이스만을 제공함&lt;/li&gt;
&lt;li&gt;RxJava는 Reactive Streams의 인터페이스들을 구현한 구현체이다.&lt;/li&gt;
&lt;li&gt;Reactive Streams는 다음 4개의 인터페이스를 제공&amp;nbsp;&lt;br /&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Publisher&lt;/li&gt;
&lt;li&gt;Subscriber&lt;/li&gt;
&lt;li&gt;Subscription&lt;/li&gt;
&lt;li&gt;Processor&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Publisher&lt;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public interface Publisher&amp;lt;T&amp;gt; {
    public void subscribe(Subscriber&amp;lt;? super T&amp;gt; s);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Publisher 인터페이스는 데이터를 생성 / 통지하는 역할을 하며, subscribe 메서드를 구현하도록 강제한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Subscriber&lt;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public interface Subscriber&amp;lt;T&amp;gt; {
    public void onSubscribe(Subscription s); // 데이터를 최초 통지할 때 호출
    public void onNext(T t); // 데이터를 통지할 때마다 호출
    public void onError(Throwable t); // 데이터 통지에 에러가 발생할 때 호출
    public void onComplete(); // 데이터 통지가 끝났을 때 호출
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subscriber 인터페이스는 통지된 데이터를 전달받아 처리하는 역할을 하며, 4개의 메서드를 구현하도록 강제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능이 많아, 주석으로 각 기능이 호출되는 시점을 명시해두었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Subscription&lt;/h4&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public interface Subscription {
    public void request(long n);
    public void cancel();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; Subscription 인터페이스는 전달 받을 데이터의 개수를 요청하고 구독을 해지하는 역할을 하며, 2개의 메서드를 구현하도록 강제한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Processor&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public interface Processor&amp;lt;T, R&amp;gt; extends Subscriber&amp;lt;T&amp;gt;, Publisher&amp;lt;R&amp;gt; {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Processor 인터페이스는 Publisher와 Subscriber의 기능을 모두 가지고 있다. 위의 코드에서 Subscriber와 Publisher를 상속하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Publisher와 Subscriber간의 프로세스 흐름&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biFK4p/btrKsNC3Oz9/qQVo7GxXbKUGECPKkrvydk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biFK4p/btrKsNC3Oz9/qQVo7GxXbKUGECPKkrvydk/img.png&quot; data-alt=&quot; publisher - subscriber간 프로세스 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biFK4p/btrKsNC3Oz9/qQVo7GxXbKUGECPKkrvydk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiFK4p%2FbtrKsNC3Oz9%2FqQVo7GxXbKUGECPKkrvydk%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;441&quot; height=&quot;590&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; publisher - subscriber간 프로세스 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 흐름으로 Reactive Streams 인터페이스가 활용되니, 인터페이스 스펙과 다이어그램을 비교해서 살펴보도록 하자.&lt;/p&gt;
&lt;h3 id=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;Cold Publisher &amp;amp; Hot Publisher&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Cold Publisher&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WIiWR/btrKzKeGjHy/wLBKkdDbOWv4mjCJqaNOwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WIiWR/btrKzKeGjHy/wLBKkdDbOWv4mjCJqaNOwk/img.png&quot; data-alt=&quot;Cold Publisher&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WIiWR/btrKzKeGjHy/wLBKkdDbOWv4mjCJqaNOwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWIiWR%2FbtrKzKeGjHy%2FwLBKkdDbOWv4mjCJqaNOwk%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;385&quot; height=&quot;395&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Cold Publisher&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Publisher는 Subscriber가 구독할 때마다 데이터를 새로 통지함&lt;/li&gt;
&lt;li&gt;즉, 데이터를 통지하는 새로운 타임라인이 생성됨&lt;/li&gt;
&lt;li&gt;Subscriber는 구독 시점과 상관없이, 통지된 데이터를 처음부터 전달받을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Hot Publisher&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;405&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Mutc/btrKA3LbugG/vzi3XeIfl4ktKdcgjtY6X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Mutc/btrKA3LbugG/vzi3XeIfl4ktKdcgjtY6X0/img.png&quot; data-alt=&quot;Hot Publisher&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Mutc/btrKA3LbugG/vzi3XeIfl4ktKdcgjtY6X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Mutc%2FbtrKA3LbugG%2Fvzi3XeIfl4ktKdcgjtY6X0%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;405&quot; height=&quot;454&quot; data-origin-width=&quot;405&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Hot Publisher&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Publisher는 Subscriber의 수와 관계없이 데이터를 한번만 통지함&lt;/li&gt;
&lt;li&gt;즉, 데이터를 통지하는 타임라인은 하나임&lt;/li&gt;
&lt;li&gt;Subscriber는 발행된 데이터를 처음부터 전달 받는 것이 아니라, 구독한 시점에 통지된 데이터들만 전달 받을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;Cold Publisher &amp;amp; Hot Publisher 예제로 살펴보기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Cold Publisher&lt;/h4&gt;
&lt;pre id=&quot;code_1661435487650&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* ColdPublisherExample.java */
import io.reactivex.Flowable;

public class ColdPublisherExample {

    public static void main(String[] args) {
        Flowable&amp;lt;Integer&amp;gt; flowable = Flowable.just(1, 3, 5, 7);

        flowable.subscribe(ColdPublisherExample::print);
        flowable.subscribe(ColdPublisherExample::print);
    }
    
    public static void print(Integer data) {
        System.out.println(&quot;data : &quot; + data);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 ColdPublisher인 Flowable을 사용한 예시이다. 결과를 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vUTkr/btrKAkmiuog/1g0i9SoBp8VnyXvGZY8nlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vUTkr/btrKAkmiuog/1g0i9SoBp8VnyXvGZY8nlK/img.png&quot; data-alt=&quot;ColdPublisherExample 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vUTkr/btrKAkmiuog/1g0i9SoBp8VnyXvGZY8nlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvUTkr%2FbtrKAkmiuog%2F1g0i9SoBp8VnyXvGZY8nlK%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;393&quot; height=&quot;232&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ColdPublisherExample 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다시피, flowable을 구독한 시점과 관계없이 1,3,5,7을 반복해서 출력한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Hot Publisher&lt;/h4&gt;
&lt;pre id=&quot;code_1661435786709&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* HotPublisherExample.java */
import io.reactivex.processors.PublishProcessor;

public class HotPublisherExample {

    public static void main(String[] args) {
        PublishProcessor&amp;lt;Integer&amp;gt; processor = PublishProcessor.create();
        processor.subscribe(data -&amp;gt; System.out.println(&quot;processor 1: &quot; + data));
        processor.onNext(1);
        processor.onNext(3);

        processor.subscribe(data -&amp;gt; System.out.println(&quot;processor 2: &quot; + data));
        processor.onNext(5);
        processor.onNext(7);
        processor.onComplete();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 HotPublisher인 Flowable을 사용한 예시이다. 결과를 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QuJ6R/btrKBrd80SJ/YcDPWC7zFvOLFiBSyGFRg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QuJ6R/btrKBrd80SJ/YcDPWC7zFvOLFiBSyGFRg1/img.png&quot; data-alt=&quot;HotPublisherExample 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QuJ6R/btrKBrd80SJ/YcDPWC7zFvOLFiBSyGFRg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQuJ6R%2FbtrKBrd80SJ%2FYcDPWC7zFvOLFiBSyGFRg1%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;394&quot; height=&quot;173&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HotPublisherExample 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HotPublisherExample에서는 processor1은 1,3,5,7 데이터를 모두 전달받았고, processor2는 데이터 1,3이 발행된 이후에 구독하였기 때문에, 5,7 두 개의 데이터만 전달받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;참고:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661437059146&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;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/u9GfD/hyPzzk4ljd/2zQ9DTPTJYKgZXjhimeIk1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/rhHjU/hyPzvCUNwc/o5C17fHs4Ta0pLWy3DNN3K/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/SADrJ/hyPzknSlU1/STw3xinsksKWUO776fVj7k/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/u9GfD/hyPzzk4ljd/2zQ9DTPTJYKgZXjhimeIk1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/rhHjU/hyPzvCUNwc/o5C17fHs4Ta0pLWy3DNN3K/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/SADrJ/hyPzknSlU1/STw3xinsksKWUO776fVj7k/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Java</category>
      <category>Cold Publisher</category>
      <category>Hot Publish</category>
      <category>publisher</category>
      <category>Reactive Streams</category>
      <category>RxJava</category>
      <category>subscriber</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/78</guid>
      <comments>https://souljit2.tistory.com/78#entry78comment</comments>
      <pubDate>Thu, 25 Aug 2022 23:14:34 +0900</pubDate>
    </item>
    <item>
      <title>[RxJava] 리액티브 프로그래밍?</title>
      <link>https://souljit2.tistory.com/77</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;목차&lt;/h3&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;#1&quot;&gt;Reactive Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot;&gt;Reactive Programming 필수 요소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot;&gt;예제&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;1&quot; data-ke-size=&quot;size23&quot;&gt;Reactive Programming?&lt;/h3&gt;
&lt;blockquote style=&quot;font-size: 14px;&quot; data-ke-style=&quot;style3&quot;&gt;In computing, reactive programming is a declarative programming paradigm concerned with&amp;nbsp;data streams and the propagation of change.&lt;br /&gt;&lt;br /&gt;컴퓨팅에서 반응형 프로그래밍은 데이터 스트림 및 변경 전파와 관련된 선언적 프로그래밍 패러다임입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게, Reactive Programming이란 데이터 변경 이벤트를 감지하여 stream으로 흘려보내는 행위를 선언적 프로그래밍 방식으로 사용하는 프로그래밍 패러다임이다.&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;b&gt;방법&lt;/b&gt;이 아니라 단순히 해야 하는 &lt;b&gt;행위&lt;/b&gt;만 서술하는 형태로 프로그래밍 하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에서 홀수만을 출력하는 다음 예시를 보면 더 쉽게 이해할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661081527650&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 홀수 출력하기 */
/* 명령형 프로그래밍 방식 */
public static void main(String[] args) {
    List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(1, 2, 3, 4, 5);

    for (int number : numbers) {
        if (number % 2 != 0)
            System.out.println(number);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661081563399&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 홀수 출력하기 */
/* 선언형 프로그래밍 방식 */
public static void main(String[] args) {
    List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(1, 2, 3, 4, 5);

    numbers.stream()
        .filter(number -&amp;gt; number % 2 != 0)
        .forEach(System.out::println);
}&lt;/code&gt;&lt;/pre&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;데이터 변경 감지 패턴의 대표적인 예가 &lt;b&gt;Push&lt;/b&gt; 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Push 방식은 데이터 변경이 발생했을 때, 변경이 발생한 곳에서 이벤트와 같은 데이터를 생성해 보내고 이 데이터를 흔히 consumer와 같은 소비 주체에서 받아 처리한다.&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;320&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckyMnC/btrKayFADRg/3Gb3qAq56wKXOKNSrsTkl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckyMnC/btrKayFADRg/3Gb3qAq56wKXOKNSrsTkl0/img.png&quot; data-alt=&quot;event push&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckyMnC/btrKayFADRg/3Gb3qAq56wKXOKNSrsTkl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckyMnC%2FbtrKayFADRg%2F3Gb3qAq56wKXOKNSrsTkl0%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;320&quot; height=&quot;203&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;event push&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 예시로 아래의 항목들이 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RTC(Real Time Communication)&lt;/li&gt;
&lt;li&gt;소켓 프로그래밍&lt;/li&gt;
&lt;li&gt;DB Trigger&lt;/li&gt;
&lt;li&gt;Spring Application Event&lt;/li&gt;
&lt;li&gt;Angular 데이터 바인딩&lt;/li&gt;
&lt;li&gt;스마트폰 Push Message&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;Reactive Programming 필수 요소&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Observable: 데이터를 발행하는 &lt;b&gt;데이터 소스&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;의 역할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;리액티브 연산자(Operators): 데이터 소스를 처리하는 함수. 데이터 소스로부터 데이터를 전달받아서 최종적인 결과 데이터를 생산한다.&lt;/li&gt;
&lt;li&gt;스케쥴러(Scheduler): &lt;b&gt;스레드 관리자&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;. 데이터 스트림을 처리할 스레드를 지정하고 관리하는 역할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Subscriber: Observable이 발행하는 데이터를 구독하는 &lt;b&gt;구독자&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;. 최종적으로 데이터를 처리하는 데이터 소비 주체이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;함수형 프로그래밍: 함수형 프로그래밍 패러다임 하에서 설계된 reactive operator를 사용하여 프로그래밍&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Observable의 사용 예시이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661089277968&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;

public class ToDoSample {

    public static void main(String[] args) throws InterruptedException {
        Observable.just(100, 200, 300, 400, 500)
            .doOnNext(ToDoSample::printDataWithThreadName)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.computation())
            .filter(num -&amp;gt; num &amp;gt; 300)
            .subscribe(ToDoSample::printDataWithThreadName);

        Thread.sleep(500);
    }

    public static void printDataWithThreadName(int data) {
        String print = String.format(&quot;%s: %d&quot;, Thread.currentThread().getName(), data);
        System.out.println(print);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;just: 데이터(100 ~ 500)를 발행한다.&lt;/li&gt;
&lt;li&gt;doOnNext: 데이터가 발행될 때, 각각의 데이터에 대해 doOnNext의 파라미터로 전달된 Consumer(printDataWithThreadName)를 실행한다.&lt;/li&gt;
&lt;li&gt;subscribeOn: 데이터 발행 스레드를 지정한다.&lt;/li&gt;
&lt;li&gt;observeOn: 데이터 가공, 구독 스레드를 지정한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와같이, Reactive Programming 요소들을 적절히 사용하면 가독성 높은 비동기 함수형 프로그래밍 코드를 손쉽게 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661124645184&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;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&quot; data-og-description=&quot;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wSHt5/hyPv8nQaYB/jOioPsUdH2Jesv0P0xnv3k/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/c5Qd6v/hyPv5dwjv9/mAdZF1GOK88tZxMUv12Wjk/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/cINLRa/hyPvVhHfMW/156s6iosKksLWSrDr4L4PK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/자바-리액티브프로그래밍-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wSHt5/hyPv8nQaYB/jOioPsUdH2Jesv0P0xnv3k/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/c5Qd6v/hyPv5dwjv9/mAdZF1GOK88tZxMUv12Wjk/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/cINLRa/hyPvVhHfMW/156s6iosKksLWSrDr4L4PK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Kevin의 알기 쉬운 RxJava 1부 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다., - 강의 소개 | 인프런...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Java</category>
      <category>Observable</category>
      <category>RxJava</category>
      <category>마블 다이어그램</category>
      <category>선언형 프로그래밍</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/77</guid>
      <comments>https://souljit2.tistory.com/77#entry77comment</comments>
      <pubDate>Sun, 21 Aug 2022 20:24:23 +0900</pubDate>
    </item>
    <item>
      <title>[회고] F-lab 멘토링(feat.NHN)</title>
      <link>https://souljit2.tistory.com/75</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6개월간의 F-lab 멘토링 회고(2021.01 ~ 2021.07)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;2021년 1월부터 7월까지, 조금 더 좋은 개발자가 되고 싶다는 마음에 F-lab 멘토링에 참여하였습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;멘토링을 받으면서 개발자가 어떤 방향으로 노력해야 하는지 깨달을 수 있었고,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;그러한 깨달음을 바탕으로 올바른 방향을 잡고 꾸준히 노력하여&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;멘토링을 시작할 때보다 조금은 더 나은 개발자가 될 수 있었다고 생각합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;또한 감사하게도, NHN에서 그간의 노력들을 좋게 봐주셔서 현재는 NHN 개발자로 재직 중에 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;저는 멘토링을 하면서 많은 도움을 받았고, 좋은 결과도 얻을 수 있었기에&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;F-lab 멘토링을 고민하고 있을 또 다른 개발자분들께 조금이나마 도움이 되고자, 저의 경험을 공유합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; 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;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;목차&lt;/h4&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;#1&quot;&gt;멘토링, 어떤 계기로 신청하게 되었나?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot;&gt;멘토링 진행 과정&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot;&gt;멘토링 종료 이후&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4&quot;&gt;F-Lab 멘토링에서 느낀 점&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5&quot;&gt;NHN에 재직하면서&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;1&quot; data-ke-size=&quot;size23&quot;&gt;멘토링, 어떤 계기로 신청하게 되었나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;F-lab을 처음 알게 된 것은 대학교 4학년 2학기 직전 방학 때였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들에게, 대학교 4학년 방학은 곧 다가올 취준 기간을 미리 준비하는 기간이기도 하죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 또한, 어떻게 취업을 하면 좋을지 막연하게 고민하고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 저는 남들보다 조금 늦게 그런 고민을 시작해서, 이것저것 프로젝트는 많이 해보았지만 정작 취업에 필요한 코딩 테스트나, 제 이력에 대한 정리가 되어 있지 않았습니다.(백엔드 개발자가 되고 싶었지만 프론트엔드 포트폴리오만 쌓여갔던 당시...)&lt;/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;그렇게 막연한 고민을 하던 와중에, 인스타그램 광고를 통해 f-lab 홍보 게시물을 발견하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 광고 내용은 기억이 나지 않지만, 스프링을 메인 기술 스택으로 하여 깊은 수준까지 공부할 것이라는 문구로 기억합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에 '대체 좋은 개발자란 무엇일까?'라는 의문과, '취업을 어떻게 하지?'라는 고민을 갖고 있었는데, f-lab의 소개 페이지를 보면서 여기에서 두 문제에 대해 한 번에 도움을 받을 수 있을 것 같았습니다.&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;아래는 제가 처음 봤던 f-lab의 소개 페이지입니다. 그때는 이런 디자인이 아니었는데, 페이지가 많이 이뻐졌네요 ㅎㅎ&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;3062&quot; data-origin-height=&quot;1498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6qNZs/btrr7qMowLU/ahMyKBDHZuP26WYai8KiIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6qNZs/btrr7qMowLU/ahMyKBDHZuP26WYai8KiIk/img.png&quot; data-alt=&quot;f-lab 소개 中&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6qNZs/btrr7qMowLU/ahMyKBDHZuP26WYai8KiIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6qNZs%2Fbtrr7qMowLU%2FahMyKBDHZuP26WYai8KiIk%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;737&quot; height=&quot;361&quot; data-origin-width=&quot;3062&quot; data-origin-height=&quot;1498&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;f-lab 소개 中&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 '좋은 개발자가 되고싶다'는 추상적인 목표가 있었는데, 해당 소개 페이지는 그 목표에 도달하는 방법을 구체적으로 풀어놓은 문장들이라는 생각이 들었고, 글만 보더라도 생각이 비슷한 부분이 많다고 느껴 멘토링을 신청하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;멘토링 진행 과정&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이론 공부&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멘토링을 시작하면, 먼저 책을 정해서 이론을 공부합니다. '자바의 신'이라는 서적으로 시작했는데, 처음부터 프로젝트를 시작하고 거기서 필요한 이론들을 보충하는 형태로 공부할 것이라 예상했던 저로서는 조금 의외였던 기억이 나네요 ㅎㅎ&lt;/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;책을 선정하면 매주 일정 분량의 책을 읽고, 주 1회 멘토님과 책의 내용에 대해 기술 면접의 형식으로 내용을 점검합니다. 이런 방식으로 내용을 점검하다 보니, 책을 잘 읽고 이해했는지, 빠뜨린 내용은 없었는지, 보충할 내용은 어떤 것들이 있을지 객관적으로 바라볼 수 있게 되면서 실력이 많이 늘었습니다.&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;자바, JSP, 객체지향 설계, 스프링 등 많은 서적을 읽고 나서 프로젝트를 시작해도 될 기초 체력이 다져지면 드디어 프로젝트용 Github Repository를 생성하게 됩니다. 다음 주에 프로젝트를 시작할 테니 프로젝트 주제를 정해오라고 하셨던 멘토님의 말씀이 되게 설레었던 기억이 나네요 :)&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프로젝트&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 시작한다 해도, 이론을 공부하는 것에는 변함이 없습니다. 프로젝트에 필요한 지식과 기술을 공부하면서 그것을 코드에 녹여내야 하고, 코드 스멜이 느껴질 때면 가감 없이 코드 리뷰에서 멘토님께 철퇴를 맞으며 더 좋은 방향으로 코드를 수정해야 했습니다.&amp;nbsp;  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Exception을 발생할 때, Exception 메시지를 자세하게 작성해야 한다는 멘토님의 코드 리뷰였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;예외는 내 메소드를 사용하는 다른 개발자와의 커뮤니케이션이다&quot;&lt;/b&gt;라는 말이 개인적으로 기억에 남는 내용이라 사진으로 공유합니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIZuNS/btrsyG04ZR1/8kUStZtJxg4IBsS1Qi7OKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIZuNS/btrsyG04ZR1/8kUStZtJxg4IBsS1Qi7OKK/img.png&quot; data-alt=&quot;Exception Message 관련 코드 리뷰&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIZuNS/btrsyG04ZR1/8kUStZtJxg4IBsS1Qi7OKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIZuNS%2FbtrsyG04ZR1%2F8kUStZtJxg4IBsS1Qi7OKK%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;858&quot; height=&quot;429&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Exception Message 관련 코드 리뷰&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이론을 공부하면서 배웠던 지식들을 코드 작성 환경에서 마주하면서, 객체지향이나 SOLID, 디자인 패턴 및 다양한 CS 지식과 같은 것들이 필요한 지 피부에 와닿게 느낄 수 있었습니다.&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;작은 라이브러리를 선택하는 순간에서도 그것과 비슷한 기능을 하는 다른 시스템은 어떤 것이 있는지, 왜 지금 꼭 이 기술을 프로젝트에 적용해야만 하는지 기술적인 근거로 설명할 수 없다면 도입할 수 없었습니다. 그래서 저는 버릇처럼, 기술을 선택할 때 가장 먼저 하는 일이 기술의 동작 원리를 파악하여 장단점과 trade-off를 분석하는 것이 되었고, 그것을 분석하는 과정에서 많은 공부가 되었습니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매주 스프린트를 반복하면서, 프로젝트의 크기를 키워 포트폴리오로 제작하고 거기에 담겨있는 기술적인 요소들도 상세하게 숙지할 수 있게 되어 취업에도 유리하게 작용할 수 있는 자료가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;멘토링 종료 이후&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자 멘토링에 참여하는 상황에 따라 다르겠지만, 저는 6개월 멘토링을 신청했는데 이 시간이 기획했던 프로젝트의 비즈니스적인 요소를 모두 완성하기에는 짧은 시간이라는 생각이 들었습니다. 이론 공부를 약 3개월가량 하고, 나머지 3개월 정도를 프로젝트에 투자했는데 당시가 학기 중이기도 했고 멘토링 외에도 다른 일을 하고 있어서 시간이 부족하더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 멘토링이 종료되는 시점에 멘토링을 연장하고 싶었지만, 신청이 너무 늦어버려서 멘토님과의 스케줄을 맞추지 못해 프로젝트의 중반부쯤 멘토링을 종료하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 멘토링을 받으면서 배운 것은 프로그래밍 지식 그 자체가 아니라 어떻게 지식에 접근하고, 그것을 고도화하여 내 것으로 만드는지에 대한 과정이었기 때문에 멘토링이 끝났어도 부족한 부분들을 스스로 채우며 프로젝트를 진행했고, 멘토링 종료 시점보다 더 확장된 애플리케이션을 제작할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctWvFM/btrsC9a47gO/U9qgWZ5NlW8TxonT9g88p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctWvFM/btrsC9a47gO/U9qgWZ5NlW8TxonT9g88p1/img.png&quot; data-alt=&quot;프로젝트 시스템 구성도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctWvFM/btrsC9a47gO/U9qgWZ5NlW8TxonT9g88p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctWvFM%2FbtrsC9a47gO%2FU9qgWZ5NlW8TxonT9g88p1%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;622&quot; height=&quot;400&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로젝트 시스템 구성도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;F-Lab 멘토링에서 느낀 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멘토링에 참여하면서, 대표적으로 다음과 같은 부분들을 배우고 느꼈습니다.&lt;/p&gt;
&lt;blockquote style=&quot;font-size: 15px;&quot; data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;지루하더라도, 이론 공부는 꾸준히 해야 한다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론 공부, 지루하고 힘든 과정이지만 백번 강조해도 지나치지 않을 정도로 중요합니다.&lt;/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;특히나 스프링처럼 규모가 큰 프레임워크를 사용하면서는, 그것을 지탱하는 많은 이론들이 기저에 있기 때문에 그것을 정확히 파악하지 않고서는 개발 중에 발생하는 문제를 이해조차 하기 어려웠어요. 처음에 스프링에서 Exception이 발생했을 때, 그 원인을 파악하기가 너무 막막했던 기억이 나네요 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링의 양이 워낙 방대해서 멘토링이 끝나고 다른 스프링 스터디에 한번 더 참여하였는데, 그때 스프링을 밑바닥부터 해체해보면서 더 깊이 공부했더니 스프링을 훨씬 정교하게 사용할 수 있게 되었고, 멘토님이 늘 이론의 중요성을 강조하신 이유를 체감할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링뿐만 아니라, 프로그램을 구성하는데 필요한 많은 분야의 이론들을 깊이 공부하고 코드에 녹여내기 위해 노력할수록, 퍼포먼스 / 가독성 / 유지보수성 등이 엄청나게 달라지는 것을 체감할 수 있었습니다. 제 코드가 바뀌는 게 보이니까 개발이 더 재밌어지고, 더 노력하고 싶은 기분도 든답니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;font-size: 15px;&quot; data-ke-style=&quot;style2&quot;&gt;기술을 깊이 파악하고, 다른 기술과 비교하여 더 나은 선택할 수 있어야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론 공부가 프로그램을 작성하기 위한 준비 단계라면, 기술을 선택하는 것은 실전 단계라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 프로그램을 구성하기 위해서는, 필요한 기술을 적절히 선택하여 알맞은 곳에 배치할 수 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하면서, 특정 부분의 문제를 해결할 수 있는 기술의 필요성을 느껴 탐색하고, 그들의 동작 원리를 파악하여 개발 중인 서비스에 최적화된 기술을 선택해야 하는 순간이 많았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 기술을 선택만 하고 끝내는 것이 아니라, 정말로 유의미한 선택을 한 것인지를 보여주는 지표를 수집하여, 눈으로 직접 확인하고 증명하면서 그렇게까지 해야 했던 이유를 논리적으로 이해할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멘토링에 참여하면서, 개발자는 이러한 부분을 깊은 곳까지 고려할 수 있어야 하고, 수많은 고민으로 하여금 프로그램을 최적화 할 수 있어야 한다는 부분을 크게 느꼈고, 기술자로서 기술을 바라보는 시야를 많이 넓힐 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;NHN에 재직하면서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나름 짧은 취준 기간(3개월)을 거쳐, 2022년 1월부터 NHN에 재직하고 있습니다. 이렇게 짧은 시간에 취업할 수 있었던 이유는 앞서 말씀드린 노력의 과정을 멘토링을 통해 미리 준비했기 때문이 아닐까 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 신입 교육을 받고 있어 부서 배치를 받기 전이라 이것저것 바쁘게 공부하는 나날을 보내고 있네요 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신입 개발자로서의 포부를 나름대로 정해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;한 줄 한 줄 살아있는 코드를 작성하는 개발자가 될 것&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;017&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/017.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/017.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 저는 개발자로 살아가면서 아무 생각없이, 아무런 고민 없이 죽어있는 코드를 만들지 않고 매번 치열하게 고민하면서 살아있는 코드를 작성하는 것이 개발자로서의 포부가 되었습니다 :)&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;꾸준히 노력하는 개발자가 되고 싶다는 마음을 담아 닉네임도 Steady로 정했답니다 ㅎㅎ&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;318&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7AHUO/btrtytF2JWA/QSqGdyOA9thgD8Q2dI9akK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7AHUO/btrtytF2JWA/QSqGdyOA9thgD8Q2dI9akK/img.png&quot; data-alt=&quot;Steady!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7AHUO/btrtytF2JWA/QSqGdyOA9thgD8Q2dI9akK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7AHUO%2FbtrtytF2JWA%2FQSqGdyOA9thgD8Q2dI9akK%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;318&quot; height=&quot;359&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Steady!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마치며&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지, 짧은 F-lab 멘토링 후기였습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;개발자로서 성장하기 위해 노력했던 스스로에게,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;또 좋은 방향성을 지속적으로 제시해준 F-lab에&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; 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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;F-lab 소개 페이지를 하단에 첨부합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://intro.f-lab.kr/?utm_source=graduate&amp;amp;utm_medium=blog&amp;amp;utm_campaign=event&amp;amp;utm_content=10&quot;&gt;https://intro.f-lab.kr/?utm_source=graduate&amp;amp;utm_medium=blog&amp;amp;utm_campaign=event&amp;amp;utm_content=10&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655178246176&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;F-Lab - 상위 1% 개발자들의 멘토링&quot; data-og-description=&quot;아마존, 구글, 페이스북, MS, 네카라쿠배 등 상위 1% IT 기업 출신 개발자들이 멘티의 성장을 도와주는 멘토링 서비스&quot; data-og-host=&quot;intro.f-lab.kr&quot; data-og-source-url=&quot;https://intro.f-lab.kr/?utm_source=graduate&amp;amp;utm_medium=blog&amp;amp;utm_campaign=event&amp;amp;utm_content=10&quot; data-og-url=&quot;https://intro.f-lab.kr/?utm_source=graduate&amp;amp;utm_medium=blog&amp;amp;utm_campaign=event&amp;amp;utm_content=10&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byeolE/hyOLgsclHe/SUv5jtgHYAb5TYiKMIxAx0/img.png?width=1024&amp;amp;height=768&amp;amp;face=0_0_1024_768&quot;&gt;&lt;a href=&quot;https://intro.f-lab.kr/?utm_source=graduate&amp;amp;utm_medium=blog&amp;amp;utm_campaign=event&amp;amp;utm_content=10&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://intro.f-lab.kr/?utm_source=graduate&amp;amp;utm_medium=blog&amp;amp;utm_campaign=event&amp;amp;utm_content=10&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byeolE/hyOLgsclHe/SUv5jtgHYAb5TYiKMIxAx0/img.png?width=1024&amp;amp;height=768&amp;amp;face=0_0_1024_768');&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;F-Lab - 상위 1% 개발자들의 멘토링&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;아마존, 구글, 페이스북, MS, 네카라쿠배 등 상위 1% IT 기업 출신 개발자들이 멘티의 성장을 도와주는 멘토링 서비스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;intro.f-lab.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;댓글에 질문이 달리는 경우가 있어, 공유 가능해보이는 내용은 여기에 공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q.&amp;nbsp;회사를&amp;nbsp;다니는&amp;nbsp;상태에서&amp;nbsp;지원하셨나요?&lt;br /&gt;A. 저는 회사를 다니는 상태는 아니었고, 취준생인 상태에서 지원하였습니다. 반면 회사를 다니시면서 참여하신 분들도 계셨던 것으로 아는데, 당시에 &lt;b&gt;새벽까지 공부하시면서&lt;/b&gt; 참여하신 것으로 기억합니다. 쉬운 과정은 아니기에, 참여하신다면 충분한 각오로 참여하셔서 많은 성과 얻으시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Q.&amp;nbsp;멘토링&amp;nbsp;2시간&amp;nbsp;만으로&amp;nbsp;일주일간&amp;nbsp;공부한&amp;nbsp;분량을&amp;nbsp;전부&amp;nbsp;커버할&amp;nbsp;수&amp;nbsp;있나요?&lt;br /&gt;A. 멘토링 시간은 일주일 1시간으로 진행됩니다. 공부한 내용들을 기준으로 &lt;b&gt;기술 면접 형태&lt;/b&gt;로 진행하기 때문에 '전부'라는 개념은 없습니다. 질문은 끝없이 나올 수 있을테니까요. 다만,  면접이 끝날 때 &lt;b&gt;내가 면접을 잘 봤는가?&lt;/b&gt;가 판단 가능한 것처럼, 멘토링 시간이 끝나고 나면 일주일동안 잘 공부했는지 판단이 됩니다.&lt;/p&gt;</description>
      <category>회고 &amp;amp; 후기</category>
      <category>f-lab</category>
      <category>f-lab 후기</category>
      <category>flab</category>
      <category>NHN</category>
      <category>nhn 합격</category>
      <category>개발자 이직</category>
      <category>개발자 취업</category>
      <category>멘토링 후기</category>
      <category>부트캠프</category>
      <category>에프랩</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/75</guid>
      <comments>https://souljit2.tistory.com/75#entry75comment</comments>
      <pubDate>Thu, 17 Feb 2022 09:53:26 +0900</pubDate>
    </item>
    <item>
      <title>[#4] Docker를 이용해 스프링부트 앱 배포하기</title>
      <link>https://souljit2.tistory.com/74</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;진행하고 있던 프로젝트가 기능 요구 사항을 어느 정도 구현하여서, 드디어 애플리케이션을&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Dockerize 하여 Google Cloud Platform에 배포하였습니다. 축하해주세요!  &lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 Docker를 처음 써보아서 힘든 점이 참 많았지만, 다양한 자료를 참고하여&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;성공적으로 배포하였고, 그 과정에서 겪었던 시행착오를 공유하고자 합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;제 글이 도움이 되어 더 빠르게 완성하시길 기원합니다!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;071&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/071.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/071.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;※ 배포 환경&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;패키징: Windows 10, openjdk 10&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;배포: Google Cloud Platform, Cent OS 7&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;목차&lt;/h3&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;#0&quot;&gt;Overall Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#1&quot;&gt;프로젝트 배포용 파일 생성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot;&gt;Docker Desktop 설치 및 Dockerfile 작성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot;&gt;Docker 이미지 생성 및 Docker hub에 push 하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4&quot;&gt;Docker hub로부터 배포 서버에 Docker 이미지 내려받기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5&quot;&gt;Docker 이미지 구동&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;0&quot; data-ke-size=&quot;size23&quot;&gt;Overall Structure&lt;/h3&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-origin-width=&quot;1198&quot; data-origin-height=&quot;582&quot; width=&quot;665&quot; height=&quot;323&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnxI8U/btrijrVdvHS/MVc4dIkUw4qh7hjsAxRv41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnxI8U/btrijrVdvHS/MVc4dIkUw4qh7hjsAxRv41/img.png&quot; data-alt=&quot;애플리케이션 배포 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnxI8U/btrijrVdvHS/MVc4dIkUw4qh7hjsAxRv41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnxI8U%2FbtrijrVdvHS%2FMVc4dIkUw4qh7hjsAxRv41%2Fimg.png&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;582&quot; width=&quot;665&quot; height=&quot;323&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;애플리케이션 배포 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전반적인 애플리케이션의 배포는 위의 사진에 나타난 과정을 따라 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 애플리케이션을 Dockerize 하기 위해 &lt;b&gt;Dockerfile을 작성&lt;/b&gt;하고, Dockerfile을 기반으로 프로젝트를 빌드하여 &lt;b&gt;Docker image화&lt;/b&gt;한 후에, 이것을 &lt;b&gt;Docker hub에 push&lt;/b&gt; &amp;amp;&lt;b&gt; 배포 서버(Google Cloud Platform. GCP)에서 pull 하여&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Docker image를 컨테이너화&lt;/b&gt;하면 배포가 완료됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;1&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 배포용 파일 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 배포를 위해, 그간 구현한 프로젝트를 &lt;b&gt;패키징 &lt;/b&gt;해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 기존에 생성되어 충돌을 일으킬 수 있는 target 파일을 제거하기 위해 mvn clean을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntelliJ의 &lt;b&gt;Maven 탭&lt;/b&gt; -&amp;gt;&lt;b&gt; Lifecycle&lt;/b&gt;에서 clean을 더블클릭하면, 별도의 명령어 없이 mvn clean을 수행할 수 있습니다. 갓텔리제이...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;996&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNyD39/btriAHpKB1B/iKN84l6mFWObTMjnUT5Mak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNyD39/btriAHpKB1B/iKN84l6mFWObTMjnUT5Mak/img.png&quot; data-alt=&quot;mvn clean&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNyD39/btriAHpKB1B/iKN84l6mFWObTMjnUT5Mak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNyD39%2FbtriAHpKB1B%2FiKN84l6mFWObTMjnUT5Mak%2Fimg.png&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;996&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;mvn clean&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 target 파일들이 삭제된 것을 확인한 후, 같은 탭의 &lt;b&gt;package&lt;/b&gt;를 더블클릭 해주시면 아래와 같이 프로젝트가 패키징 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LGQN1/btriBva7xSt/LELvXPBJQhnNISecPQcadK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LGQN1/btriBva7xSt/LELvXPBJQhnNISecPQcadK/img.png&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;630&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; style=&quot;width: 68.6964%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LGQN1/btriBva7xSt/LELvXPBJQhnNISecPQcadK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLGQN1%2FbtriBva7xSt%2FLELvXPBJQhnNISecPQcadK%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;1302&quot; height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G2ryY/btriArOhlDB/dkblEJOMLbSg0PIXCVGPx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G2ryY/btriArOhlDB/dkblEJOMLbSg0PIXCVGPx1/img.png&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;429&quot; style=&quot;width: 30.1409%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G2ryY/btriArOhlDB/dkblEJOMLbSg0PIXCVGPx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG2ryY%2FbtriArOhlDB%2FdkblEJOMLbSg0PIXCVGPx1%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;389&quot; height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;패키징 완료!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;Docker Desktop 설치 및 Dockerfile 작성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;169&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mv6jR/btriDxfbCd5/WtCKYEIAsvkzPy96ejjO80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mv6jR/btriDxfbCd5/WtCKYEIAsvkzPy96ejjO80/img.png&quot; data-alt=&quot;Dockerize&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mv6jR/btriDxfbCd5/WtCKYEIAsvkzPy96ejjO80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmv6jR%2FbtriDxfbCd5%2FWtCKYEIAsvkzPy96ejjO80%2Fimg.png&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;169&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Dockerize&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 패키징을 했다면, 패키징 한 파일을 &lt;b&gt;Dockerize 해야&lt;/b&gt; 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerize에 대한 간략한 설명을 &lt;b&gt;더보기&lt;/b&gt;에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerize란, Docker build를 통해 프로젝트 파일을 &lt;b&gt;Docker image화&lt;/b&gt; 한 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 Docker image화 되면, 해당 파일을 Docker hub에 push 하여 docker가 설치된 어떤 환경에서도 동일하게 실행할 수 있습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerize를 위해, 먼저 &lt;a href=&quot;https://www.docker.com/products/docker-desktop&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker desktop&lt;/a&gt;을 설치합니다. (※ Docker Desktop 설치를 위해 &lt;b&gt;wsl2&lt;/b&gt;가 PC에 설치되어 있어야 합니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;494&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JnMbr/btriDyLVril/mx71XSXaz8ILBHMPLXsaw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JnMbr/btriDyLVril/mx71XSXaz8ILBHMPLXsaw0/img.png&quot; data-alt=&quot;Docker 설치 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JnMbr/btriDyLVril/mx71XSXaz8ILBHMPLXsaw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJnMbr%2FbtriDyLVril%2Fmx71XSXaz8ILBHMPLXsaw0%2Fimg.png&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;494&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 설치 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 설치를 완료하고 커맨드 라인에서 &lt;b&gt;docker&lt;/b&gt;를 입력했을 때, 아래와 같이 명령어 소개 페이지가 나온다면 정상적으로 설치된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;612&quot; width=&quot;760&quot; height=&quot;488&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPRtMA/btriB5i8PbC/RChKJjxqu5SLYjSFKI1qAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPRtMA/btriB5i8PbC/RChKJjxqu5SLYjSFKI1qAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPRtMA/btriB5i8PbC/RChKJjxqu5SLYjSFKI1qAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPRtMA%2FbtriB5i8PbC%2FRChKJjxqu5SLYjSFKI1qAk%2Fimg.png&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;612&quot; width=&quot;760&quot; height=&quot;488&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker가 정상적으로 설치되었다면, &lt;a href=&quot;https://spring.io/guides/gs/spring-boot-docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Boot with Docker 페이지&lt;/a&gt;를 참고하여 Dockerfile을 프로젝트 루트 디렉토리에 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;386&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIBtdK/btriDwHlDuq/Tx9QeeMLadYqPdNqYCjqk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIBtdK/btriDwHlDuq/Tx9QeeMLadYqPdNqYCjqk0/img.png&quot; data-alt=&quot;Dockerfile 작성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIBtdK/btriDwHlDuq/Tx9QeeMLadYqPdNqYCjqk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIBtdK%2FbtriDwHlDuq%2FTx9QeeMLadYqPdNqYCjqk0%2Fimg.png&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;386&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Dockerfile 작성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 버전에 따라&amp;nbsp;&lt;a href=&quot;https://hub.docker.com/_/openjdk&quot;&gt;https://hub.docker.com/_/openjdk&lt;/a&gt;를 참고하여 자신에게 필요한 jdk 버전을 FROM ~ 부분에 작성합니다. 저는 openjdk:11-jdk 버전으로 Dockerfile을 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;Docker 이미지 생성 및 Docker hub에 push 하기&lt;/h3&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-origin-width=&quot;779&quot; data-origin-height=&quot;582&quot; width=&quot;504&quot; height=&quot;376&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OyFfL/btriFia0Lho/glnp3eES2hNsQkTwhIaBc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OyFfL/btriFia0Lho/glnp3eES2hNsQkTwhIaBc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OyFfL/btriFia0Lho/glnp3eES2hNsQkTwhIaBc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOyFfL%2FbtriFia0Lho%2Fglnp3eES2hNsQkTwhIaBc0%2Fimg.png&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;582&quot; width=&quot;504&quot; height=&quot;376&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile을 작성하였으니, 프로젝트를 빌드 및 이미지화 하여 Docker hub에 push 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker hub 공식 페이지&lt;/a&gt;에 접속하여 계정을 생성하고, 이미지를 업로드할 Repository를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;323&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pnPYw/btriFivjFma/IWSeUt1o8J9oBZBjjRiwFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pnPYw/btriFivjFma/IWSeUt1o8J9oBZBjjRiwFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pnPYw/btriFivjFma/IWSeUt1o8J9oBZBjjRiwFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpnPYw%2FbtriFivjFma%2FIWSeUt1o8J9oBZBjjRiwFK%2Fimg.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;323&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅을 위해 &lt;b&gt;test-repo&lt;/b&gt; Repository를 생성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;455&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mn7km/btriArU48OK/R5kjtuNHUvWMZMFRrjlt0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mn7km/btriArU48OK/R5kjtuNHUvWMZMFRrjlt0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mn7km/btriArU48OK/R5kjtuNHUvWMZMFRrjlt0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmn7km%2FbtriArU48OK%2FR5kjtuNHUvWMZMFRrjlt0k%2Fimg.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;455&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;884&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5zLmk/btriBARfGSb/DFcaCrDKaVcuvuFXR9ldb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5zLmk/btriBARfGSb/DFcaCrDKaVcuvuFXR9ldb0/img.png&quot; data-alt=&quot;test-repo 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5zLmk/btriBARfGSb/DFcaCrDKaVcuvuFXR9ldb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5zLmk%2FbtriBARfGSb%2FDFcaCrDKaVcuvuFXR9ldb0%2Fimg.png&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;884&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;test-repo 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적당한 이름의 Repository를 생성하였다면, 다시 프로젝트로 돌아가 프로젝트를 빌드(이미지화)합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile이 위치한 프로젝트 루트 폴더에서 터미널을 실행한 후, 아래 명령어를 입력하면, 도커 이미지가 생성됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634994049152&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// docker build -t {docker hub 계정명}/{docker hub repository명} .
docker build -t souljit1/test-repo .&lt;/code&gt;&lt;/pre&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-origin-width=&quot;996&quot; data-origin-height=&quot;615&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/stqxO/btriBB3LKko/d01vKEp16mJ2kTFmjGE6Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/stqxO/btriBB3LKko/d01vKEp16mJ2kTFmjGE6Gk/img.png&quot; data-alt=&quot;Docker 이미지 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/stqxO/btriBB3LKko/d01vKEp16mJ2kTFmjGE6Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FstqxO%2FbtriBB3LKko%2Fd01vKEp16mJ2kTFmjGE6Gk%2Fimg.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;615&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 이미지 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 이미지가 정상적으로 생성되었다면, docker push 명령어를 통해 생성한 Repository로 이미지를 push 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634994233155&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// docker push {docker hub 계정명}/{docker hub repository명}:{tagname(optional)}
docker push souljit1/test-repo&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;267&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlzX8c/btriA4LGK1l/olWKc2pdIBuPeWDzUsSuCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlzX8c/btriA4LGK1l/olWKc2pdIBuPeWDzUsSuCK/img.png&quot; data-alt=&quot;Docker 이미지 push&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlzX8c/btriA4LGK1l/olWKc2pdIBuPeWDzUsSuCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlzX8c%2FbtriA4LGK1l%2FolWKc2pdIBuPeWDzUsSuCK%2Fimg.png&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;267&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 이미지 push&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;Docker hub로부터 배포 서버에 Docker 이미지 내려받기&lt;/h3&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-origin-width=&quot;768&quot; data-origin-height=&quot;582&quot; width=&quot;444&quot; height=&quot;336&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DCawi/btriAGR1H6g/qrUTop44NdALhsvbIkosfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DCawi/btriAGR1H6g/qrUTop44NdALhsvbIkosfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DCawi/btriAGR1H6g/qrUTop44NdALhsvbIkosfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDCawi%2FbtriAGR1H6g%2FqrUTop44NdALhsvbIkosfk%2Fimg.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;582&quot; width=&quot;444&quot; height=&quot;336&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지를 Docker hub에 업로드하였으니, 배포 서버에서 해당 이미지를 내려받아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 배포 서버는 CentOS 7 환경이므로, 이에 맞는 명령어를 사용하여 진행합니다.&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;먼저, 배포 서버 내에 아래 명령어를 통해 docker를 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634996252279&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo yum install docker&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;648&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uq4a3/btriCFYTUKP/WwAX9PITOc4ZhusMpJTMu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uq4a3/btriCFYTUKP/WwAX9PITOc4ZhusMpJTMu0/img.png&quot; data-alt=&quot;Docker 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uq4a3/btriCFYTUKP/WwAX9PITOc4ZhusMpJTMu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuq4a3%2FbtriCFYTUKP%2FWwAX9PITOc4ZhusMpJTMu0%2Fimg.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;648&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker가 정상적으로 설치되면, 아래의 명령어로 도커를 실행하고, 실행 상태를 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634996425755&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo systemctl start docker # docker 실행
$ sudo systemctl status docker # docker 상태 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 초록색 글씨 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;active(running)&lt;/b&gt;&lt;/span&gt;가 보인다면, docker가 정상적으로 실행된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;333&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VPh1d/btriBdaWsq1/2xNdIJxSeL3GonQ4SqA7kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VPh1d/btriBdaWsq1/2xNdIJxSeL3GonQ4SqA7kK/img.png&quot; data-alt=&quot;Docker 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VPh1d/btriBdaWsq1/2xNdIJxSeL3GonQ4SqA7kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVPh1d%2FbtriBdaWsq1%2F2xNdIJxSeL3GonQ4SqA7kK%2Fimg.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;333&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker가 정상적으로 실행되었으니, 아래의 명령어로 Docker hub에 업로드 해 둔 Docker 이미지 파일을 내려받도록 합시다.&lt;/p&gt;
&lt;pre id=&quot;code_1634996636385&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ # sudo docker pull {계정명}/{repository명}
$ sudo docker pull souljit1/test-repo&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;213&quot; width=&quot;770&quot; height=&quot;231&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5qyLy/btriHDyZXn7/LzB6PtmkQoadPYMBFjTOr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5qyLy/btriHDyZXn7/LzB6PtmkQoadPYMBFjTOr0/img.png&quot; data-alt=&quot;Docker 이미지 내려받기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5qyLy/btriHDyZXn7/LzB6PtmkQoadPYMBFjTOr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5qyLy%2FbtriHDyZXn7%2FLzB6PtmkQoadPYMBFjTOr0%2Fimg.png&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;213&quot; width=&quot;770&quot; height=&quot;231&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 이미지 내려받기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;Docker 이미지 구동&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;327&quot; width=&quot;328&quot; height=&quot;199&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVl3Xg/btriBdPAt9O/khnrSJXo9KBtnk46SY0VKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVl3Xg/btriBdPAt9O/khnrSJXo9KBtnk46SY0VKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVl3Xg/btriBdPAt9O/khnrSJXo9KBtnk46SY0VKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVl3Xg%2FbtriBdPAt9O%2FkhnrSJXo9KBtnk46SY0VKk%2Fimg.png&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;327&quot; width=&quot;328&quot; height=&quot;199&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 이미지를 내려받았으니, 아래의 명령어로 이를 실행하여 컨테이너화 하고, 정상적으로 실행되었는지 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634997204890&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ # sudo nohup docker run -p {host 포트}/{container 포트} {docker hub 계정명}/{docker hub repository명} &amp;gt; nohup.out 2&amp;gt;&amp;amp;1 &amp;amp;
$ sudo nohup docker run -p 8081:8080 souljit1/test-repo &amp;gt; nohup.out 2&amp;gt;&amp;amp;1 &amp;amp;

$ sudo docker ps # 도커 컨테이너 목록 확인&lt;/code&gt;&lt;/pre&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-origin-width=&quot;1180&quot; data-origin-height=&quot;455&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PYBcx/btriArOkTJE/CkVWWEkEf1fZt4wRtSdOZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PYBcx/btriArOkTJE/CkVWWEkEf1fZt4wRtSdOZ1/img.png&quot; data-alt=&quot;Docker 컨테이너 구동 및 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PYBcx/btriArOkTJE/CkVWWEkEf1fZt4wRtSdOZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPYBcx%2FbtriArOkTJE%2FCkVWWEkEf1fZt4wRtSdOZ1%2Fimg.png&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;455&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker 컨테이너 구동 및 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ -p 옵션에 대한 설명은 아래의 더보기를 참고하시기 바랍니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;margin: 20px 0px; caret-color: auto; background-color: #fafafa; padding: 20px 20px 22px; border: 1px dashed #dddddd; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;line-height: 1.7; margin-top: 0px; margin-bottom: 0px;&quot; data-ke-size=&quot;size16&quot;&gt;docker run 명령어의 -p 옵션에서, host 포트와 container 포트를 지정합니다.&lt;/p&gt;
&lt;p style=&quot;line-height: 1.7; margin: 1px auto 0px;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같이 -p 8081:8080으로 지정한 경우,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;컨테이너를 실행한&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;host의 8081 포트에 포워딩되며, 내부 컨테이너의 8080 포트에 포워딩됩니다.&lt;/p&gt;
&lt;p style=&quot;line-height: 1.7; margin: 1px auto 0px;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 외부에서 배포된 서버에 접속하기 위해서는, {host ip}:{host port}로 접근하면 해당 요청을 host가 container에 바인딩하여 접속하는 형태입니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, 컨테이너 목록에 등록했던 이미지가 발견되면 정상적으로 컨테이너가 구동된 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱을 백그라운드로 띄우고, 로그를 nohup.out에서 확인하도록 하였으니 nohup.out의 내용을 확인하면 서버에서 남긴 로그를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634999002763&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cat nohup.out&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;382&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPot6G/btriAF6JBwy/b0sBlqCSPzNFJbDvwkFyfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPot6G/btriAF6JBwy/b0sBlqCSPzNFJbDvwkFyfK/img.png&quot; data-alt=&quot;서버 배포 완료!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPot6G/btriAF6JBwy/b0sBlqCSPzNFJbDvwkFyfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPot6G%2FbtriAF6JBwy%2Fb0sBlqCSPzNFJbDvwkFyfK%2Fimg.png&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;382&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 배포 완료!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이상으로, 프로그램을 패키징 및 이미지화하고&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;GCP 인스턴스에 배포하는 과정을 진행하였습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다음으로는, 배포된 앱을 대상으로 &lt;b&gt;nGrinder&lt;/b&gt;를 통해 &lt;b&gt;스트레스 테스트&lt;/b&gt;를 진행하며&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;서버가 어느 정도까지의 부하를 견디는지 확인하고,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;서버를 &lt;b&gt;scale-out 하여&lt;/b&gt; 인스턴스를 증가시키면서&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;어떠한 변화가 생기는지 직접 확인하는 작업을 계획하고 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;다시금 유의미한 지표를 발견했을 때,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;좋은 글로 공유할 수 있도록 노력하겠습니다!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;041&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/041.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/041.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;#img&quot;&gt;이미지&lt;/a&gt; 출처 및 참고 강의: &lt;a href=&quot;https://class101.net/products/T6HT0bUDKIH1V5i3Ji2M&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;대기업 개발자 푸와 함께하는 백엔드 시스템 실무&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1634740164671&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;현직 대기업 개발자 푸와 함께하는 진짜 백엔드 시스템 실무!&quot; data-og-description=&quot;IT 대기업들의 백엔드 개발자 채용공고를 본 적 있나요?  위 밑줄 친 부분을 배울 수 있도록 커리큘럼을 구성했어요. 겉보기엔 어려워 보이지만, 결국 배우고 나면 나도 저런 곳에서 일할 수 있&quot; data-og-host=&quot;class101.net&quot; data-og-source-url=&quot;https://class101.net/products/T6HT0bUDKIH1V5i3Ji2M&quot; data-og-url=&quot;https://class101.net/products/T6HT0bUDKIH1V5i3Ji2M&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/blj4Y3/hyL3t08BbJ/UMzLrkhk5y5Vp3lxkNzRq0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/IzaZR/hyL3n0VkBV/gPesntLKKLcuqkhKrNk1gK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://class101.net/products/T6HT0bUDKIH1V5i3Ji2M&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://class101.net/products/T6HT0bUDKIH1V5i3Ji2M&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/blj4Y3/hyL3t08BbJ/UMzLrkhk5y5Vp3lxkNzRq0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/IzaZR/hyL3n0VkBV/gPesntLKKLcuqkhKrNk1gK/img.jpg?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;현직 대기업 개발자 푸와 함께하는 진짜 백엔드 시스템 실무!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;IT 대기업들의 백엔드 개발자 채용공고를 본 적 있나요?  위 밑줄 친 부분을 배울 수 있도록 커리큘럼을 구성했어요. 겉보기엔 어려워 보이지만, 결국 배우고 나면 나도 저런 곳에서 일할 수 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;class101.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 URL: &lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/f-lab-edu/sell-everything&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1634740189552&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - f-lab-edu/sell-everything: Spring Framework 기반 중고 거래 서비스 플랫폼&quot; data-og-description=&quot;Spring Framework 기반 중고 거래 서비스 플랫폼. Contribute to f-lab-edu/sell-everything development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/f-lab-edu/sell-everything&quot; data-og-url=&quot;https://github.com/f-lab-edu/sell-everything&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dUuYD6/hyL3sA9UUu/vUTid36PbM2akCkUdwfpK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/f-lab-edu/sell-everything&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dUuYD6/hyL3sA9UUu/vUTid36PbM2akCkUdwfpK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - f-lab-edu/sell-everything: Spring Framework 기반 중고 거래 서비스 플랫폼&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Framework 기반 중고 거래 서비스 플랫폼. Contribute to f-lab-edu/sell-everything development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Sell-everything</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/74</guid>
      <comments>https://souljit2.tistory.com/74#entry74comment</comments>
      <pubDate>Wed, 20 Oct 2021 20:29:43 +0900</pubDate>
    </item>
    <item>
      <title>[#3] 그래서 DispatcherServlet이 대체 뭐하는 녀석이죠?</title>
      <link>https://souljit2.tistory.com/73</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 MVC를 통해 처음 스프링을 접하면서, 굉장히 혼란스러웠던 기억이 납니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;의존성 주입, AOP, PSA, Application Context 등 스프링을 구성하는&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;방대한 용어들이 쏟아지니, 초심자 입장에서 쉽지 않았던 경험이었네요.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;076&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/076.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/076.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;그중에서도, 유독 이해가 되지 않던 것이 &lt;b&gt;DispatcherServlet&lt;/b&gt;이란 친구였습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;DispatcherServlet의 작동원리니, FrontController니 하는 일련의 개념들에 대해서는 각각 이해할 수 있어도,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래서 DispatcherServlet이 왜 존재하는데?&lt;/b&gt;라는 의문은 항상 남아있었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;최근에 Servlet을 자세히 공부하면서, 왜 스프링 MVC가 DispatcherServlet을 사용하는지&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이해하게 되었고, 그 이유를 아래와 같은 짧은 문장으로 얘기할 수 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;text-align: center;&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개발자에게 서블릿 컨텍스트 구현의 책임이 아닌,&lt;br /&gt;스프링 컨텍스트 구현의 책임을 주는 것&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이렇게만 이야기하면 추상적인 느낌이 강할 수 있기 때문에,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 구체적으로 서블릿을 탐구하면서 위의 문장을 발견해보도록 하겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;함께 가시죠!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;025&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/025.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/025.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; 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;style6&quot; /&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;목차&lt;/h4&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;#1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Servlet?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GenericServlet과 HttpServlet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HttpServlet의 동작 과정&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Servlet Container와 Spring Container를 연결하는 Dispatcher Servlet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DispatcherServlet은 Servlet Container와 Spring Container를 어떻게 연결할까?&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;1&quot; data-ke-size=&quot;size20&quot;&gt;Servlet?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;서블릿은 JavaEE에 종속된 기술로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.oracle.com/javaee/5/tutorial/doc/bnafe.html&quot;&gt;JavaEE docs&lt;/a&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;에서 그 정의를 살펴보면 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote style=&quot;font-size: 15px;&quot; data-ke-style=&quot;style3&quot;&gt;A servlet is a Java programming language class that is used to extend the capabilities of servers that host applications accessed by means of a request-response programming model. Although servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers.&lt;br /&gt;&lt;br /&gt;서블릿은 요청-응답 프로그래밍 모델을 통해 액세스 되는 응용 프로그램을 호스팅 하는 서버의 기능을 확장하는 데 사용되는 Java 프로그래밍 언어 클래스입니다. 서블릿은 모든 유형의 요청에 응답할 수 있지만 일반적으로 웹 서버에서 호스팅 하는 애플리케이션을 확장하는 데 사용됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버의 기능을 확장한다&lt;/b&gt;는 말이 조금 난해한데, 서블릿을 사용하는 패턴을 기반으로 추측해보면 서버에서 다양한 요청을 처리하는 것에는 굉장히 많은 로우 레벨 기술을 필요로 하는데, 그러한 일련의 기술을 서블릿이 제공하는 기능을 통해 선언적인 방식으로 구현할 수 있다는 말로 이해했습니다. JavaEE의 기술적인 목표가&amp;nbsp;&lt;b&gt;로우 레벨 기술&lt;/b&gt;을 개발자 대신 구현해서 더 쉽게 기술을 사용할 수 있게 하는 것이었으니, 충분히 이러한 관점으로 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿은 모든 타입의 요청에 응답할 수 있지만, 일반적으로 웹 서버에서 호스팅 하는 애플리케이션을 확장하는 데 사용된다고 합니다. 그래서 많은 사람들이 서블릿을 좁은 의미로 설명할 때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자바 기반의 동적 웹 페이지를 제공하기 위한 기술&lt;/b&gt;이라고 설명하기도 합니다.&lt;/p&gt;
&lt;h4 id=&quot;2&quot; data-ke-size=&quot;size20&quot;&gt;GenericServlet과 HttpServlet&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;조금 더 구체적으로 서블릿을 어떻게 사용하는지 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;서블릿을 작성하기 위해서, javax.servlet과 javax.servlet.http에서 제공하는 클래스나 인터페이스를 상속해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;HTTP 기반이 아닌 &lt;b&gt;일반 서비스 구현을 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;GenericServlet&lt;/b&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스를 확장하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;HTTP 기반의 서비스를 구현하기 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;GenericServlet&lt;span style=&quot;color: #24292f;&quot;&gt;을 확장한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;HttpServlet&lt;/b&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스&lt;/b&gt;를 확장합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;GenericServlet은 서블릿의 생명주기 관리 및 기능에 대한 코드를 작성하도록 하고, &lt;/span&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;HttpServlet은 일반적인 서블릿의 기능과 HTTP와 관련한 메서드, 이를테면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;doGet&lt;span style=&quot;color: #24292f;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;doPost&lt;span style=&quot;color: #24292f;&quot;&gt;의 메서드 등을 추가적으로 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;아래의 구체적인 서블릿 클래스 코드를 보시면, 어떠한 방식으로 서블릿을 작성해야 하는지 감이 잡힐 것이라 생각합니다.&lt;/span&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1631594268072&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* GenericServlet.java */
/* 주로 서블릿의 생명주기 관리 메서드를 작성 */
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {

    ...
    public void destroy() {
    }
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1631594287453&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* HttpServlet.java */
/* Http 관련 기능을 확장한 Servlet */
public abstract class HttpServlet extends GenericServlet {
    ...
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString(&quot;http.method_get_not_supported&quot;);
        this.sendMethodNotAllowed(req, resp, msg);
    }
    ...
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString(&quot;http.method_post_not_supported&quot;);
        this.sendMethodNotAllowed(req, resp, msg);
    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString(&quot;http.method_put_not_supported&quot;);
        this.sendMethodNotAllowed(req, resp, msg);
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3&quot; data-ke-size=&quot;size20&quot;&gt;HttpServlet의 동작 과정&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOHp3F/btre4OqDiW3/fmlixMzDXbtMIEB1LffRA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOHp3F/btre4OqDiW3/fmlixMzDXbtMIEB1LffRA1/img.png&quot; data-alt=&quot;HttpServlet 동작 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOHp3F/btre4OqDiW3/fmlixMzDXbtMIEB1LffRA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOHp3F%2Fbtre4OqDiW3%2FfmlixMzDXbtMIEB1LffRA1%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;642&quot; height=&quot;264&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HttpServlet 동작 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿을 통해, 웹 서버가 클라이언트 요청을 처리하는 과정을 요약하면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트로부터 HTTP Request 전송&lt;/li&gt;
&lt;li&gt;서블릿 컨테이너(ex. Tomcat)는 요청을 전송받아,&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;HttpServletRequest,&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;HttpServletResponse&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;객체 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;web.xml에 정의된 url pattern을 확인하고, 요청을 처리할 서블릿 검색 or 생성&lt;/li&gt;
&lt;li&gt;서블릿 컨테이너는 요청을 처리할 Thread 객체를 생성하고, 해당 서블릿 객체의 service() 메서드 호출&lt;/li&gt;
&lt;li&gt;service 메서드 호출 후, Http 요청 메서드에 따라 doGet(), doPost() 등의 메서드 호출&lt;/li&gt;
&lt;li&gt;요청 처리가 완료되면, HttpServletRequest 및 HttpServletResponse 객체 소멸&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면, HTTP 요청마다 요청을 처리할 서블릿을 생성하고, 생성한 서블릿을 요청에 맵핑하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;HTTP Request를 처리하는 Actor&lt;/b&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;여기까지가 서블릿을 통해 Http 요청을 처리하던 방식입니다. 하지만, 위의 패턴은 서블릿을 상속하는 방식이기 때문에, 개발자의 코드를 &lt;b&gt;서블릿&lt;/b&gt;이라는 기술에 종속되게 한다는 큰 문제점이 있습니다. JavaEE가 사장되어 현재는 거의 사용하지 않는 이유도, 코드의 기술 결합도를 너무 높여서 재사용성을 떨어뜨린다는 것이었는데, 서블릿에서도 그 모습을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 이러한 문제점을, 컨테이너 계층을 &lt;b&gt;Servlet Container - Spring Container&lt;/b&gt;로 분리하고 DispatcherServlet을 통해 연결하여 해결합니다.&lt;/p&gt;
&lt;h4 id=&quot;4&quot; data-ke-size=&quot;size20&quot;&gt;Servlet Container와 Spring Container를 연결하는 DispatcherServlet&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eehuqV/btre4N6pOJF/isNoBSC9B0IbcDlAubcf10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eehuqV/btre4N6pOJF/isNoBSC9B0IbcDlAubcf10/img.png&quot; data-alt=&quot;스프링 MVC 서버 구성도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eehuqV/btre4N6pOJF/isNoBSC9B0IbcDlAubcf10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeehuqV%2Fbtre4N6pOJF%2FisNoBSC9B0IbcDlAubcf10%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;627&quot; height=&quot;167&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스프링 MVC 서버 구성도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;앞서 말했듯, Servlet Container는 &lt;b&gt;Http 요청과 Servlet을 맵핑&lt;/b&gt;하여 처리합니다. 그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;JavaEE 등의&lt;span style=&quot;color: #24292f;&quot;&gt; 레거시에서는 Servlet 추상 클래스의 구현체를 만들어 사용했으나, 스프링 MVC에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;DispatcherServlet&lt;/b&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;이라는 &lt;b&gt;하나의 서블릿&lt;/b&gt;을 통해 모든 요청을 수신하고, 적당한 &lt;b&gt;Controller Layer(ex. Controller- Service - DAO)를 검색하여 비즈니스 로직을 처리&lt;/b&gt;한 후 결과를 반환하는 형태로 요청을 처리합니다. 이러한 방식을,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;FrontController 패턴&lt;/b&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;이라고 부릅니다.&lt;/span&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;773&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ2hqE/btre4OqG9za/AINmg1eNFkjoxHeW2gMIpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ2hqE/btre4OqG9za/AINmg1eNFkjoxHeW2gMIpk/img.png&quot; data-alt=&quot;FrontController&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ2hqE/btre4OqG9za/AINmg1eNFkjoxHeW2gMIpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ2hqE%2Fbtre4OqG9za%2FAINmg1eNFkjoxHeW2gMIpk%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;548&quot; height=&quot;283&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FrontController&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;스프링 MVC의 DispatcherServlet이 &lt;b&gt;Servlet 구현의 책임을 가져가고&lt;/b&gt;, 개발자에게는 서블릿이 아닌&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;스프링 컨텍스트 작성&lt;/b&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;&lt;b&gt;의 책임&lt;/b&gt;을 주었기 때문에, 기존 JavaEE에 종속된 프로그램을 만들어야 했던 패턴을 벗어나 지금의 &lt;b&gt;POJO 지향 프로그램&lt;/b&gt; 개발이 가능했다고 생각되네요!&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;DispatcherServlet은 Servlet Container와 Spring Container를 어떻게 연결할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, DispatcherServlet이 Servlet Container와 Spring Container를 어떻게 연결하는지 궁금해집니다.&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;DispatcherServlet은 Servlet Container에 포함된 개념이기 때문에, DispatcherServlet에서 Spring Container를 참조할 수 있기만 한다면, 두 컨테이너의 연결이 성립한다고 볼 수 있겠습니다. Spring Container란, 사실상&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ApplicationContext&lt;/b&gt;를 의미한다고 볼 수 있죠. 다시 말해, Spring Container를 참조할 수 있다는 것은 ApplicationContext를 알고 있다는 것이고, 이러한 맥락에서 클래스에 ApplicationContext를 주입하는 인터페이스인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ApplicationContextAware&lt;/b&gt;를 떠올릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 수준에서 이를 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631596973216&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 생성자 주입을 통해 ApplicationContext를 주입 */
public class DispatcherServlet extends FrameworkServlet {
    ...
    public DispatcherServlet(WebApplicationContext webApplicationContext) {
        super(webApplicationContext);
        this.setDispatchOptionsRequest(true);
    }
    ...
}

/* ApplicaionContextAware를 구현하여 런타임에 ApplicationContext 의존성 주입이 일어남. */
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;DispatcherServlet의 계층구조를 보면, DispatcherServlet이&amp;nbsp;&lt;/span&gt;&lt;b&gt;ApplicationContextAware&lt;/b&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;를 구현하고 있고, 생성자 주입을 통해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;WebApplicationContext&lt;span style=&quot;color: #24292f;&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;820&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mhNF5/btreYn80blI/zU2FgYKQd5KmL16uPDGhx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mhNF5/btreYn80blI/zU2FgYKQd5KmL16uPDGhx0/img.png&quot; data-alt=&quot;DispatcherServlet 계층 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mhNF5/btreYn80blI/zU2FgYKQd5KmL16uPDGhx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmhNF5%2FbtreYn80blI%2FzU2FgYKQd5KmL16uPDGhx0%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;820&quot; height=&quot;547&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DispatcherServlet 계층 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;위와 같은 방식으로, Servlet Container와 Spring Container는 연결되어 작동합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마치며&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;DispatcherServlet이 어떠한 방식으로 작동하여, 기존 서블릿의 문제점을 해결하는지 살펴보았습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;스프링이 기존 엔터프라이즈 시스템 개발 패턴의 문제를 해결해나가는 과정들을 살펴보다 보면,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;하나같이 &lt;b&gt;'객체지향'&lt;/b&gt; 설계를 기반으로 문제를 풀어나간다는 공통점을 발견할 수 있었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;스프링이 '서버 프레임워크'가 아니라, '객체지향 프레임워크'임을 느끼게 되는 순간이&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;스프링을 더 잘 사용할 수 있게 되는 때가 아닐까 생각되네요 :)&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;002&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/002.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/002.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&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;프로젝트 URL : &lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/f-lab-edu/sell-everything&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1631597850051&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - f-lab-edu/sell-everything: Spring Framework 기반 중고 거래 서비스 플랫폼&quot; data-og-description=&quot;Spring Framework 기반 중고 거래 서비스 플랫폼. Contribute to f-lab-edu/sell-everything development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/f-lab-edu/sell-everything&quot; data-og-url=&quot;https://github.com/f-lab-edu/sell-everything&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsW45/hyLBt8XYZP/zKBGVx2LCRtEhoRedkQe7k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/f-lab-edu/sell-everything&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsW45/hyLBt8XYZP/zKBGVx2LCRtEhoRedkQe7k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - f-lab-edu/sell-everything: Spring Framework 기반 중고 거래 서비스 플랫폼&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Framework 기반 중고 거래 서비스 플랫폼. Contribute to f-lab-edu/sell-everything development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Sell-everything</category>
      <category>Dispatcher Servlet</category>
      <category>genericservlet</category>
      <category>httpservlet</category>
      <category>JavaEE</category>
      <category>servlet</category>
      <category>spring</category>
      <category>spring boot</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/73</guid>
      <comments>https://souljit2.tistory.com/73#entry73comment</comments>
      <pubDate>Tue, 14 Sep 2021 13:25:46 +0900</pubDate>
    </item>
    <item>
      <title>[#2] Redis 캐시를 통해 읽기 성능 향상하기</title>
      <link>https://souljit2.tistory.com/72</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 스프링 캐시를 적용하면서 읽기 성능을 향상했던 경험을 공유하고자 합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;나름대로, 캐시란 무엇인가에 대해 정의를 내려보았습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터의 실시간성을 약간 포기하고,&lt;br /&gt;&lt;br /&gt;큰 성능적 이점을 얻는 것&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;물론 캐시는 이미 공식적인 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%BA%90%EC%8B%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정의&lt;/a&gt;가 존재하지만,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 정의에서는 &lt;b&gt;'실시간성을 포기한다'&lt;/b&gt;라는 trade off를&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;추론을 통해 알아내야 했기 때문에,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;그 부분까지 확실하게 함께 담아 정리해보았답니다.  &lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;그럼, 캐시를 적용하기까지 과정은 어땠는지,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;어떻게 캐시를 통해 성능을 개선했는지&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이야기해보도록 하겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;034&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/034.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/034.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;목차&lt;/b&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;a href=&quot;#1&quot;&gt;캐시?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2&quot;&gt;Spring Cache Abstraction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3&quot;&gt;Local Cache vs Global Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4&quot;&gt;캐싱 적용하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5&quot;&gt;캐싱 &amp;amp; 성능 확인&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;1&quot; data-ke-size=&quot;size20&quot;&gt;캐시?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위키백과에서 찾아본 캐시의 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%BA%90%EC%8B%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정의&lt;/a&gt;는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote style=&quot;font-size: 15px;&quot; data-ke-style=&quot;style3&quot;&gt;캐시(cache, 문화어: 캐쉬, 고속완충기, 고속완충기억기)는 컴퓨터 과학에서 데이터나 값을 미리 복사해 놓는 임시 장소를 가리킨다. 캐시는 캐시의 접근 시간에 비해 원래 데이터를 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우에 사용한다. 캐시에 데이터를 미리 복사해 놓으면 계산이나 접근 시간 없이 더 빠른 속도로 데이터에 접근할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 캐시는, 접근 비용이 비싼 데이터의 &lt;b&gt;사본&lt;/b&gt;을 만들어 저장하고, 동일한 요청이 있을 때 원본 데이터에 접근하지 않고 사본 데이터를 제공할 수 있게 하는 &lt;b&gt;중간 장치&lt;/b&gt;의 개념입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 통신, 하드 디스크 접근 등 컴퓨터 시스템의 곳곳에서 &lt;b&gt;병목&lt;/b&gt;이 일어날 수 있을만한 지점의 성능 향상을 위해 캐시를 사용하곤 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 진행 중인 &lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로젝트&lt;/a&gt;에서도 캐싱을 적용할만한 지점을 발견했는데, 바로 &lt;b&gt;데이터베이스&lt;/b&gt;에 저장된 데이터를 가져와 클라이언트에게 제공하는 과정이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 데이터를 저장하기 위해 MySQL 데이터베이스를 사용하고 있는데, MySQL은 &lt;b&gt;하드디스크 기반&lt;/b&gt;의 데이터 스토리지이기 때문에 서버의 메모리보다 훨씬 느린 속도로 데이터를 탐색합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에, 클라이언트의 동일한 읽기 요청에 대해 데이터베이스를 매번 접근하지 않고, 처음에 접근해 얻은 데이터의 사본을 &lt;b&gt;메모리&lt;/b&gt;에 저장하고 정해진 유효기간동안 사용한다면 기존의 데이터베이스에서 매번 원본 데이터를 가져오는 것보다 성능상의 이점이 클 것이라 생각했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;628&quot; data-filename=&quot;DB Caching.png&quot; width=&quot;619&quot; height=&quot;306&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAFA4c/btrevGAt1vJ/Wb4XKy6dXWC1PYFvdXqcRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAFA4c/btrevGAt1vJ/Wb4XKy6dXWC1PYFvdXqcRK/img.png&quot; data-alt=&quot;DB Caching flow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAFA4c/btrevGAt1vJ/Wb4XKy6dXWC1PYFvdXqcRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAFA4c%2FbtrevGAt1vJ%2FWb4XKy6dXWC1PYFvdXqcRK%2Fimg.png&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;628&quot; data-filename=&quot;DB Caching.png&quot; width=&quot;619&quot; height=&quot;306&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;DB Caching flow&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 id=&quot;2&quot; data-ke-size=&quot;size20&quot;&gt;Spring Cache Abstraction&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/cache.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Cache Manager&lt;/a&gt;의 문서에서는, 스프링이&amp;nbsp;&lt;b&gt;AOP&lt;/b&gt;를 사용하여 &lt;b&gt;비침투적으로&lt;/b&gt; 캐싱을 적용한다고 설명합니다.&lt;/p&gt;
&lt;blockquote style=&quot;font-size: 15px;&quot; data-ke-style=&quot;style3&quot;&gt;Both the &lt;b&gt;cache:annotation-driven element&lt;/b&gt; and &lt;b&gt;@EnableCaching annotation&lt;/b&gt; allow various options to be specified that influence the way the caching behavior is &lt;b&gt;added to the application through AOP&lt;/b&gt;. The configuration is intentionally similar with that of &lt;b&gt;@Transactional&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설명을 읽어보면, &lt;b&gt;@Transactional&lt;/b&gt; 애너테이션을 통해 트랜잭션 서비스를 제공하는 것과 비슷한 패턴으로 캐싱을 적용한다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 특정 기술을 추상화 계층으로 가져가고, 개발자에게는 기술을 사용하는 인터페이스를 동일한 형태로 제공함으로써 기술의 벤더가 변경된다고 할 지라도 코드에는 전혀 영향을 미치지 않게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를테면 스프링 캐시를 관리하는 &lt;b&gt;CacheManager&lt;/b&gt;에는 ConcurrentMapCacheManager, SimpleCacheManager, EhCacheManager, RedisCacheManager 등 다양한 기술 벤더가 존재하는데, 이들 모두 &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/CacheManager.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CacheManager&lt;/a&gt; 인터페이스를 구현하고 있기 때문에 우리는 &lt;b&gt;동일한 시그니쳐&lt;/b&gt;로 캐시 기능을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 방법으로, 프로그램이 특정 기술에 종속되지 않게 하는 방식을 &lt;b&gt;PSA(Portable Service Abstraction)&lt;/b&gt;라고 하며, 스프링은 이를 스프링의 3대 요소 중 하나로 꼽습니다.&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;스프링 PSA 덕분에, 우리는 서버의 환경에 맞추어서 필요한 캐시 기술을 선택할 수 있고, 캐시 설정만 바꾸어주면 어떠한 캐시 기술로든 전환할 수 있습니다.&lt;/p&gt;
&lt;h4 id=&quot;3&quot; data-ke-size=&quot;size20&quot;&gt;Local Cache vs Global Cache&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링의 PSA 덕분에, 우리는 서비스 환경에 따라 어떤 Cache 전략이던 CacheManager를 구현하고 있기만 하다면 유연하게 우리 코드에 적용할 수 있습니다.&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;그럼 어떠한 Cache 기술을 적용할지 고려해보아야 되겠죠? Cache 관리 전략을 선택할 때 가장 먼저 고려해야 할 요소는, 캐시 데이터를 저장할 스토리지를 서버가 자체적으로 소유하고 있을지, 외부 서버에 캐시 저장소를 따로 둘 지에 대한 부분입니다. 캐시 저장소를 서버에 두는 방식을 Local Cache, 외부 캐시 저장소를 두는 방식을 Global Cache라고 합니다.&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-origin-width=&quot;724&quot; data-origin-height=&quot;594&quot; data-filename=&quot;Local Global.png&quot; width=&quot;342&quot; height=&quot;281&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blIDZb/btreB7YOZZr/ai81I7etvTlANkQUk7Xov1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blIDZb/btreB7YOZZr/ai81I7etvTlANkQUk7Xov1/img.png&quot; data-alt=&quot;Local Cache vs Global Cache&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blIDZb/btreB7YOZZr/ai81I7etvTlANkQUk7Xov1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblIDZb%2FbtreB7YOZZr%2Fai81I7etvTlANkQUk7Xov1%2Fimg.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;594&quot; data-filename=&quot;Local Global.png&quot; width=&quot;342&quot; height=&quot;281&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Local Cache vs Global Cache&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 위치의 차이로 인해, Local Cache와 Global Cache는 다양한 상황에서 성능적 차이를 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local Cache와 Global Cache의 특징을 각각 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Local Cache의 특징&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 조회 오버헤드가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 데이터를 서버 메모리상에 두는 것의 가장 큰 장점은, 무엇보다도 속도가 빠르다는 점입니다. 캐시를 외부 저장소에 저장하면 네트워크 통신을 통해 캐시 저장소에 접근하고, 데이터를 가져오는 과정 등의 오버헤드가 없기 때문에 Local Cache의 데이터 읽기 속도는 현저히 빠릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;서버 간 데이터 일관성이 깨질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local Cache는 단일 서버 인스턴스에 캐시 데이터를 저장하기 때문에, 서버의 인스턴스가 여러 개일 경우 서버 간 캐시 데이터가 일치하지 않아 신뢰성을 떨어뜨릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;서버간 동기화가 어렵고, 동기화 비용이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 일관성을 유지하기 위해 동기화를 한다고 하더라도, 추가적인 비용이 발생합니다. 더군다나 서버의 개수가 늘어날수록, 자신을 제외한 모든 인스턴스와 동기화 작업을 해야 하기 때문에 비용의 크기는 서버의 개수의 제곱에 비례하여 증가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Global Cache의 특징&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크 I/O 비용 발생&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;Global Cache는 외부 캐시 저장소에 접근하여 데이터를 가져오기 때문에, 이 과정에서 네트워크 I/O가 비용이 발생합니다. 하지만 서버 인스턴스가 추가될 때에도 동일한 비용만을 요구하기 때문에, 서버가 고도화될수록 더 높은 효율을 발휘합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 18px;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 일관성을 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Global Cache는 모든 서버의 인스턴스가 동일한 캐시 저장소에 접근하기 때문에, 데이터의 일관성을 보장할 수 있습니다. 데이터의 일관성이 깨지기 쉬운 분산 서버 환경에 적합한 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Local Cache vs Global Cache, 어떤 기준으로 선택해야 할까?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local Cache와 Global Cache의 특성을 고려했을 때, 어떤 기술을 선택해야 할지에 대한 기준은 &quot;&lt;b&gt;데이터의 일관성이 깨져도 비즈니스에 영향을 주지 않는가?&quot;라고&lt;/b&gt; 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를테면, 사용자 정보가 변경되어 프로필에 반영되어야 하는 상황을 가정할 때, 서버 간 동기화가 맞지 않아서 프로필에 반영되는데 시간이 조금 걸린다 하더라도, 전체적인 서비스 운영에 큰 타격을 주지는 않습니다. 금전이 오고가는 문제도 아니고, 프로필의 정보가 조금 늦게 반영된다고 해서 큰 문제가 발생하지 않으니까요. 이러한 경우에는 서버간 동기화 없이 &lt;b&gt;서버 자체적으로 로컬 캐싱&lt;/b&gt;을 하는 것도 괜찮은 선택지라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, &lt;b&gt;상품 데이터&lt;/b&gt;를 캐싱한다고 했을 때는 상황이 달라집니다. 사용자가 가격을 변경했는데, 그것이 반영되지 않으면 서비스 신뢰를 심각하게 손상하고, 운이 나쁘면 법적 문제로 이어지기도 합니다. 따라서, 이러한 경우에는 동기화가 속도보다 더 중요하며, 그렇기에 동기화가 확실하게 보장되는 Global Cache를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;진행 중인 프로젝트에서는 &lt;b&gt;게시글과 댓글&lt;/b&gt;에 캐싱을 적용하였고, 둘 다 데이터의 일관성이 중요하다고 판단되어 &lt;b&gt;Global Cache&lt;/b&gt;인 Redis를 적용하였습니다. 추후 성능 테스트를 진행하면서, Local Cache를 적용해 성능을 향상할 수 있는 지점을 발견하면,&amp;nbsp;&lt;b&gt;CompositeCacheManager&lt;/b&gt;를 사용해 2차 캐시를 구성해보고, 관련한 내용을 새로 포스팅하도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;038&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/038.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/038.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;4&quot; data-ke-size=&quot;size20&quot;&gt;캐싱 적용하기&lt;/h4&gt;
&lt;blockquote style=&quot;font-size: 15px;&quot; data-ke-style=&quot;style2&quot;&gt;본 예시는 Redis가 설치된 환경을 가정하여 진행합니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1631257989001&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* RedisConfig.java */
@Configuration
@EnableRedisHttpSession
public class RedisConfig {

    /* DateTimeFormat을 String 형태로 지원하기 위한 ObjectMapper */
    @Autowired
    private ObjectMapper objectMapper;

    @Value(&quot;${spring.redis.host}&quot;)
    private String redisHost;

    @Value(&quot;${spring.redis.port}&quot;)
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

    @Bean
    public RedisCacheManager redisCacheManager() {

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
            .defaultCacheConfig()
            .disableCachingNullValues()
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper))
            );

        Map&amp;lt;String, RedisCacheConfiguration&amp;gt; redisCacheConfigurationMap = new HashMap&amp;lt;&amp;gt;();
        redisCacheConfigurationMap
            .put(CacheNames.POST, redisCacheConfiguration.entryTtl(Duration.ofMinutes(5)));

        return RedisCacheManager.RedisCacheManagerBuilder
            .fromConnectionFactory(redisConnectionFactory())
            .withInitialCacheConfigurations(redisCacheConfigurationMap)
            .cacheDefaults(redisCacheConfiguration)
            .build();

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1631259083812&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* MapperConfig.java */
@Configuration
public class MapperConfig {

    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss&quot;);

    @Bean
    public ObjectMapper serializingObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        /*
        */
        objectMapper.registerModules(javaTimeModule, new Jdk8Module());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }

    public class LocalDateTimeSerializer extends JsonSerializer&amp;lt;LocalDateTime&amp;gt; {

        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(FORMATTER));
        }
    }

    public class LocalDateTimeDeserializer extends JsonDeserializer&amp;lt;LocalDateTime&amp;gt; {

        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return LocalDateTime.parse(p.getValueAsString(), FORMATTER);
        }
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Cache를 적용하기 위해, &lt;b&gt;RedisCacheManager&lt;/b&gt; 및 &lt;b&gt;RedisConnectionFactory&lt;/b&gt;를 등록해주면 Redis 캐시 설정이 완료됩니다. 제 경우에는 데이터에 LocalDateTime format이 있는데, 해당 타입이 String 형태로 나오지 않아 이것을 String 형태로 변환해주기 위해 ObjectMapper를 따로 구성하여 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, 메인 파일에 &lt;b&gt;@EnableCaching&lt;/b&gt; 애너테이션을 작성하여 스프링에게 캐싱 적용 대상임을 알려줍니다. 해당 애너테이션을 작성하면, 스프링은 AOP를 통해 비 침투적으로 캐싱 전략을 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1631259671624&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableCaching
@SpringBootApplication
public class SellEverythingApplication {

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

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음, 캐싱을 적용할 지점에 @Cacheable을 지정해주고, 캐싱이 삭제되어야 하는 지점에 @CacheEvict를 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1631259913688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@AllArgsConstructor
public class PostServiceImpl implements PostService {
    ...
    @Cacheable(value = CacheNames.POST)
    @Override
    public List&amp;lt;PostVO&amp;gt; getPostsByQueryString(Map&amp;lt;String, String&amp;gt; queryMap) {
        return postMapper.getPosts(queryMap);
    }

    @CacheEvict(value = CacheNames.POST, allEntries = true)
    @Override
    public void createPost(PostDTO newPost) {
        int memberIdBySession = authService.getRequestMemberId();
        postMapper.createPost(PostDTO.builder()
            .postTitle(newPost.getPostTitle())
            .postContents(newPost.getPostContents())
            .postItemName(newPost.getPostItemName())
            .postItemPrice(newPost.getPostItemPrice())
            .postCategory(newPost.getPostCategory())
            .memberIdFk(memberIdBySession)
            .build()
        );
    }

    @CacheEvict(value = CacheNames.POST, allEntries = true)
    @Override
    public void updatePostById(int id, PostDTO newPost) {
        int sessionMemberId = sessionAuthService.getRequestMemberId();
        postMapper.updatePostById(id, sessionMemberId, newPost);
    }

    @CacheEvict(value = CacheNames.POST, allEntries = true)
    @Override
    public void deletePostById(int id) {
        int sessionMemberId = sessionAuthService.getRequestMemberId();
        postMapper.deletePostById(id, sessionMemberId);
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Cacheable과 @CacheEvict의 value 파라미터로 &lt;b&gt;캐시 이름&lt;/b&gt;을 지정할 수 있습니다. Redis는 여기에 지정된 이름을 기반으로 key-value쌍의 캐시 데이터를 저장합니다.&lt;/p&gt;
&lt;h4 id=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;캐싱 &amp;amp; 성능 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐싱을 적용했으니, 정말로 캐싱이 되고 있는지 확인해봐야겠죠? Redis는 redis-cli라는 인터페이스를 제공하여 캐시를 눈으로 확인할 수 있도록 지원합니다. redis-cli를 설치하고, &lt;b&gt;keys *&lt;/b&gt; 명령어를 커맨드 라인에 입력하면 현재 저장된 모든 캐시를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;282&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VsnYA/btreJwYGaUg/KK14NAkWG0cxKyBxKfJ9g0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VsnYA/btreJwYGaUg/KK14NAkWG0cxKyBxKfJ9g0/img.png&quot; data-alt=&quot;빈 캐시 데이터 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VsnYA/btreJwYGaUg/KK14NAkWG0cxKyBxKfJ9g0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVsnYA%2FbtreJwYGaUg%2FKK14NAkWG0cxKyBxKfJ9g0%2Fimg.png&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;282&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;빈 캐시 데이터 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 아무런 조회도 하지 않았기 때문에 당연히 캐시는 비어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 게시글 데이터 조회 요청을 보내고, 다시 캐시를 확인해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;498&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brJ0nq/btreKJQzNmI/M7KU0bKqFKiAAKPnGNDrBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brJ0nq/btreKJQzNmI/M7KU0bKqFKiAAKPnGNDrBk/img.png&quot; data-alt=&quot;서버에 게시글 데이터 요청&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brJ0nq/btreKJQzNmI/M7KU0bKqFKiAAKPnGNDrBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrJ0nq%2FbtreKJQzNmI%2FM7KU0bKqFKiAAKPnGNDrBk%2Fimg.png&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;498&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버에 게시글 데이터 요청&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;296&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cukdxs/btreEJLMq3Y/aGyGnDpygY534fi5uCBnek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cukdxs/btreEJLMq3Y/aGyGnDpygY534fi5uCBnek/img.png&quot; data-alt=&quot;게시글 캐시가 적재된 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cukdxs/btreEJLMq3Y/aGyGnDpygY534fi5uCBnek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcukdxs%2FbtreEJLMq3Y%2FaGyGnDpygY534fi5uCBnek%2Fimg.png&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;296&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;게시글 캐시가 적재된 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;게시글을 가져오는데 180ms가 소요되었고, Redis에 캐시가 저장된 것을 확인할 수 있습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 캐시를 통해서 조회 성능이 정말 좋아지는 지도 확인해야겠죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;461&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvqTYg/btreKlJaK5I/6xlnMVE56rJFyhce4sX6hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvqTYg/btreKlJaK5I/6xlnMVE56rJFyhce4sX6hK/img.png&quot; data-alt=&quot;읽기 속도가 약 5배 향상됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvqTYg/btreKlJaK5I/6xlnMVE56rJFyhce4sX6hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvqTYg%2FbtreKlJaK5I%2F6xlnMVE56rJFyhce4sX6hK%2Fimg.png&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;461&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;읽기 속도가 약 5배 향상됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 게시글 조회 요청에 비해, 소요 시간이 180ms -&amp;gt; 39ms로 약 5배가량의 성능 향상을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마치며&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 캐시를 적용하여, API의 읽기 성능을 향상하였습니다. 이러한 경험을 통해 몇 가지 깨달음을 얻게 되어, 공유하며 글을 마치고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 개발자는 &lt;b&gt;비즈니스 요구사항&lt;/b&gt;을 명확하게 이해하고 있어야 합니다. Local Cache와 Global Cache 중, 어떤 것을 선택할지 결정하는 과정에서 &lt;b&gt;비즈니스적인 요소&lt;/b&gt;가 의사 결정에 큰 영향을 미쳤습니다. 개발자는 필드에서 수많은 서비스 요구사항을 만나게 될 텐데, 그때마다 비즈니스 요구사항을 명확하게 이해하여서 이를 적합한 기술에 담아낼 수 있어야 기술과 비즈니스, 두 마리 토끼를 모두 잡을 수 있겠다고 생각했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째, 개발자는 기술을 &lt;b&gt;아주&lt;/b&gt; 깊게 이해해야 합니다. 기술을 적용할 때 어떤 선택지가 있는지, 지금 이 기술을 선택했을 때 어떤 &lt;b&gt;trade-off&lt;/b&gt;가 있는지를 기술 작동 원리에 기반하여 샅샅이 알고 있어야 앞서 말씀드린 비즈니스 요구사항에 적합한 기술을 선택할 수 있고, 그래야 더 괜찮은 성능을 발휘하는 서버 구성이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 매주, 기술에 대해 공부하고 토론을 나누는 &lt;a href=&quot;https://github.com/caffeine-library&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;기술 서적 스터디&lt;/a&gt;에 참여하고 있습니다. 매 스터디마다 여기까지 공부한다고?라는 생각이 들 정도로 모든 지식을 파헤치면서 스터디를 진행하고 있는데, 시간이 지나면서 눈에 띄게 성장하는 스스로의 모습을 발견할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 읽게 되시는 개발자분들도, 기술을 깊게 파헤치고 그것을 적용하면서 즐거움을 느껴보시면 좋을 것 같다는 짧은 의견 드리며, 이만 글을 마치겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;034&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/034.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/034.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 URL : &lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/f-lab-edu/sell-everything&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1631262539685&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - f-lab-edu/sell-everything: Spring Framework 기반 중고 거래 서비스 플랫폼&quot; data-og-description=&quot;Spring Framework 기반 중고 거래 서비스 플랫폼. Contribute to f-lab-edu/sell-everything development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/f-lab-edu/sell-everything&quot; data-og-url=&quot;https://github.com/f-lab-edu/sell-everything&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/irjAI/hyLyRozliw/7a7z8K2YcGUDUDuchM1k40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/f-lab-edu/sell-everything&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/f-lab-edu/sell-everything&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/irjAI/hyLyRozliw/7a7z8K2YcGUDUDuchM1k40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - f-lab-edu/sell-everything: Spring Framework 기반 중고 거래 서비스 플랫폼&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Framework 기반 중고 거래 서비스 플랫폼. Contribute to f-lab-edu/sell-everything development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Sell-everything</category>
      <category>Cache</category>
      <category>cacheManager</category>
      <category>redis cache</category>
      <category>spring</category>
      <category>Spring Cache</category>
      <category>캐시</category>
      <category>캐싱</category>
      <author>개발하자</author>
      <guid isPermaLink="true">https://souljit2.tistory.com/72</guid>
      <comments>https://souljit2.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 7 Sep 2021 20:33:54 +0900</pubDate>
    </item>
  </channel>
</rss>