<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>제닌의 블로그</title>
    <link>https://jeanin-blog.tistory.com/</link>
    <description>개발자가 되고 싶은 계발자 </description>
    <language>ko</language>
    <pubDate>Sat, 9 May 2026 17:03:41 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Jeanin</managingEditor>
    <image>
      <title>제닌의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/5119905/attach/a85a1edfb714413386d586ac29cb3763</url>
      <link>https://jeanin-blog.tistory.com</link>
    </image>
    <item>
      <title>[WIL] Redis 분산 락</title>
      <link>https://jeanin-blog.tistory.com/22</link>
      <description>&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 교육 과정 수강중 작성된 학습노트입니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;❔WIL(Weekly I Learned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;분산 락(Distributed&amp;nbsp;Lock)이란?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vKv3F/btsPUJAHVjR/6h5e8bkosRz8AFU6w0LjVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vKv3F/btsPUJAHVjR/6h5e8bkosRz8AFU6w0LjVK/img.png&quot; data-alt=&quot;출처: https://mohammedev.hashnode.dev/distributed-locks&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vKv3F/btsPUJAHVjR/6h5e8bkosRz8AFU6w0LjVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvKv3F%2FbtsPUJAHVjR%2F6h5e8bkosRz8AFU6w0LjVK%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;800&quot; height=&quot;461&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://mohammedev.hashnode.dev/distributed-locks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;분산 시스템에서 서로 다른 서버 인스턴스에 대한 일관된 락을 제공하기 위한 장치이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여러 서버가 동시에 공유 자원에 접근&lt;/b&gt;할 때, 한 시점에는 &lt;b&gt;오직 하나의 주체만 &lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해당 자원을 변경할 수 있도록 하는 동기화 메커니즘&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단일 서버 환경에서는 DB락 같은 방식으로 동시성 제어가 가능하지만,&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마이크로서비스(MSA), 멀티 서버 환경, 클러스터 환경에서는&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버 간의 메모리를 공유하지 않기 때문에 이런 방식이 통하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Redis(레디스)&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpr33u/btsPTzFrdeO/ki7JXRh3l73okOTzmYLV01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpr33u/btsPTzFrdeO/ki7JXRh3l73okOTzmYLV01/img.png&quot; data-alt=&quot;출처: https://namu.wiki/w/Redis&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpr33u/btsPTzFrdeO/ki7JXRh3l73okOTzmYLV01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpr33u%2FbtsPTzFrdeO%2Fki7JXRh3l73okOTzmYLV01%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;639&quot; height=&quot;255&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://namu.wiki/w/Redis&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfyByx/btsPWq70fec/JzAEbrY8GtXyRG7K4dZljK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfyByx/btsPWq70fec/JzAEbrY8GtXyRG7K4dZljK/img.png&quot; data-alt=&quot;이전 로고( ~2024.04)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfyByx/btsPWq70fec/JzAEbrY8GtXyRG7K4dZljK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfyByx%2FbtsPWq70fec%2FJzAEbrY8GtXyRG7K4dZljK%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;131&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이전 로고( ~2024.04)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 락을 구현하기 위해 많이 쓰이는 도구 중 하나가 &lt;b&gt;Redis&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 기반의 &lt;b&gt;Key-Value 저장소&lt;/b&gt;이다. NoSQL DB.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠른 속도와 다양한 자료구조 지원 덕분에 &lt;b&gt;분산 락, 캐시, 세션 저장소, 메시지 브로커&lt;/b&gt; 등 여러 용도로 쓰인다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;레디스의 Lock 3가지 방식&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Simple Lock&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock 획득 실패 시 비즈니스 로직을 수행하지 않음&lt;/p&gt;
&lt;pre id=&quot;code_1755335914491&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;락 획득 여부 = redis 에서 key 로 확인
if (락 획득) {
	try {
	  로직 실행
	} finally {
	  lock 제거
	}
} else {
  throw Lock 획득 실패 예외
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: right;&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;b&gt;Spin Lock&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock 획득 실패 시 일정 횟수 재시도&lt;/p&gt;
&lt;pre id=&quot;code_1755336013109&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;재시도 횟수 = 0
while (true) {
  락 획득 여부 = redis 에서 key 로 확인 // SETNX key &quot;1&quot;
  if (락 획득) {
		try {
		  로직 실행
		} finally {
		  lock 제거
		}
		break;
  } else {
	  재시도 횟수 ++
	  if (재시도 횟수 == 최대 횟수) throw Lock 획득 실패 예외
	  시간 지연 ( 대기 )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&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;b&gt;Pub/Sub&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock 획득 실패 시 &quot;구독&quot;하고, 차례를 기다림&lt;/p&gt;
&lt;pre id=&quot;code_1755336156075&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;락 획득 여부 = redis 에서 lock 데이터 에 대한 subscribe 요청 및 획득 시 값 반환
// 특정 시간 동안 Lock 에 대해 구독
if (락 획득) {
	로직 실행
} else {
	// 정해진 시간 내에 Lock 을 획득하지 못한 경우
	throw Lock 획득 실패 예외
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: right;&quot;&gt;항해플러스 발제자료 발췌&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qR0zu/btsPUFFbZKI/mnjt0x5hCgKiLcgYgMMNd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qR0zu/btsPUFFbZKI/mnjt0x5hCgKiLcgYgMMNd0/img.png&quot; data-alt=&quot;출처: 같은 팀원이 AI로 생성한 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qR0zu/btsPUFFbZKI/mnjt0x5hCgKiLcgYgMMNd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqR0zu%2FbtsPUFFbZKI%2Fmnjt0x5hCgKiLcgYgMMNd0%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;800&quot; height=&quot;513&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: 같은 팀원이 AI로 생성한 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;락과 트랜잭션의 순서&lt;/h2&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;락&lt;/span&gt; 획득 ➡️ &lt;span style=&quot;color: #006dd7;&quot;&gt;트랜잭션&lt;/span&gt; 시작 ➡️ 비즈니스 로직 수행 ➡️ &lt;span style=&quot;color: #006dd7;&quot;&gt;트랜잭션&lt;/span&gt; 종료 ➡️ &lt;span style=&quot;color: #f89009;&quot;&gt;락&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 이미지 처럼 &lt;span style=&quot;color: #f89009;&quot;&gt;락&lt;/span&gt; 해제를 먼저하고 &lt;span style=&quot;color: #006dd7;&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-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QXk2L/btsPUK7kw8b/CdeSww2MFZEqgeyeN1CnGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QXk2L/btsPUK7kw8b/CdeSww2MFZEqgeyeN1CnGk/img.png&quot; data-alt=&quot;출처: https://helloworld.kurly.com/blog/distributed-redisson-lock/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QXk2L/btsPUK7kw8b/CdeSww2MFZEqgeyeN1CnGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQXk2L%2FbtsPUK7kw8b%2FCdeSww2MFZEqgeyeN1CnGk%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;800&quot; height=&quot;748&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://helloworld.kurly.com/blog/distributed-redisson-lock/&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;⚠️ 락 해제가 먼저되고 커밋되면, &lt;b&gt;데이터 정합성&lt;/b&gt;에 문제가 있을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;재고가 총 8개가 되어야 하는데, 데이터 정합성이 깨져서 재고가 9개가 될 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 처럼 분산 락 구현 시에 순서가 중요하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;트랜잭션 범위를 락 범위가 감싸야 정상적으로 재고 차감이 이루어질 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZzOK2/btsPWqNM4Y4/0qTrFLrsQW3krljC4BYRx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZzOK2/btsPWqNM4Y4/0qTrFLrsQW3krljC4BYRx1/img.png&quot; data-alt=&quot;출처: https://helloworld.kurly.com/blog/distributed-redisson-lock/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZzOK2/btsPWqNM4Y4/0qTrFLrsQW3krljC4BYRx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZzOK2%2FbtsPWqNM4Y4%2F0qTrFLrsQW3krljC4BYRx1%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;800&quot; height=&quot;771&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://helloworld.kurly.com/blog/distributed-redisson-lock/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;과제 피드백 회고&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;멀티 락 사용의 실수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis로 분산 락을 AOP 방식으로 구현해서 주문 및 결제 기능에 적용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 멀티 락 사용을 하는데 있어, 키 전략에서 실수가 있었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;유저ID 1번, 상품ID 100번&lt;br /&gt;유저ID 100번 상품ID 1번&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;위 와 같은 상황일 경우에는 유저ID가 100번인 경우 불필요한 대기가 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1755339073863&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class OrderFacade {

    private final OrderService orderService;
    private final CouponService couponService;
    private final ProductService productService;
    private final UserService userService;

    @DistributedLock(lockType = LockType.ORDER, keys = {
        &quot;#command.userId()&quot;,	// ⚠️ 유저ID를 키에 넣은 게 실수!
        &quot;#command.OrderItemCommands().![productId]&quot;
    })
    @Transactional
    public OrderResult placeOrderWithPayment(CreateOrderCommand command) {
        // 1. 쿠폰 처리
        BigDecimal discountRate = BigDecimal.ZERO;
        if (command.couponId() != null){
            discountRate = couponService.applyCouponForOrder(command.couponId());
        }

        // 2. 주문 총액 사전 계산 및 잔액 검증
        BigDecimal totalAmount = productService.calculateTotalAmount(command.OrderItemCommands(), discountRate);
        userService.validateBalance(command.userId(), totalAmount);
        
        // 3. 상품 재고 차감
        List&amp;lt;Product&amp;gt; persistedProducts = productService.decreaseProductStocks(command.OrderItemCommands());
        
        // 4. OrderItem 리스트 변환
        List&amp;lt;OrderItem&amp;gt; orderItems = orderService.createOrderItems(command.OrderItemCommands(), persistedProducts);
        
        // 5. OrderService를 통한 주문 생성
        OrderResult orderResult = orderService.createOrder(command, discountRate, orderItems);

        // 6. 유저 잔액 차감 (이미 검증됨)
        userService.useBalance(command.userId(), orderResult.paidPrice());

        return orderResult;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;KPT회고&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Keep&amp;nbsp;&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;락&amp;nbsp;구현만&amp;nbsp;하고&amp;nbsp;싶었는데,&amp;nbsp;캐싱까지&amp;nbsp;구현했음&amp;nbsp;(캐싱&amp;nbsp;구현하려면&amp;nbsp;인기상품&amp;nbsp;조회&amp;nbsp;기능&amp;nbsp;개발해야했어서..하하)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL과 Redis를 테스트 컨테이너에서 실행하려는데, 설정이 꼬여서 많이 어려웠음&lt;/li&gt;
&lt;li&gt;STEP09-10(이전 과제) 브랜치에서 작업하다가 커밋하는 어메이징한 실수를 저질러서 과제 제출 전에 멘탈이 나갈 뻔했음 :)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Try&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;피드백 반영하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <category>Wil</category>
      <category>항해99</category>
      <category>항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/22</guid>
      <comments>https://jeanin-blog.tistory.com/22#entry22comment</comments>
      <pubDate>Sat, 16 Aug 2025 19:18:05 +0900</pubDate>
    </item>
    <item>
      <title>[WIL] DB Lock을 이용한 동시성 제어(Java, Spring)</title>
      <link>https://jeanin-blog.tistory.com/21</link>
      <description>&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 교육 과정 수강중 작성된 학습노트입니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;❔WIL(Weekly I Learned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;동시성 제어(Concurrency&amp;nbsp;Control)&lt;/h2&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: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;동시성 문제&lt;/h3&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: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여러 트랜잭션(또는 스레드)이 DB에 접근하려고 하면 어떻게 될까?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;상품 재고를 차감하려는 여러 트랜잭션이 있다면?&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; 트랜잭션(Transaction)이란?&lt;br /&gt;더 이상 나눌 수 없는 하나의 완결된 작업 단위&lt;br /&gt;모든 과정이 완료되어야 의미가 있는 작업의 최소 단위&lt;br /&gt;일반적으로는 데이터베이스의 트랜잭션을 지칭하지만, CS 전반에서 사용되는 개념&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oB6HL/btsPO62Zkq7/RWW3IAqjUFy4MW0MK8TlK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oB6HL/btsPO62Zkq7/RWW3IAqjUFy4MW0MK8TlK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oB6HL/btsPO62Zkq7/RWW3IAqjUFy4MW0MK8TlK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoB6HL%2FbtsPO62Zkq7%2FRWW3IAqjUFy4MW0MK8TlK1%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;800&quot; height=&quot;450&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품 재고를 차감하려면 먼저 현재 재고 상태를 `SELECT(조회)` 해와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시의 2개의 트랜잭션이 현재 재고 수량을 10개로 조회했다면, 2개 중 하나의 트랜잭션은 작업 내용을 잃게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1번: 재고 10개로 조회해서 1개 차감&lt;/li&gt;
&lt;li&gt;2번: 재고 10개로 조회해서 1개 차감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 재고가 최종적으로 8개여야 하는데, 2번 트랜잭션에 대한 작업 내용 분실됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnAQ7o/btsPLIv5Kae/KsiJrSOoe0eJ0V2YJkdrG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnAQ7o/btsPLIv5Kae/KsiJrSOoe0eJ0V2YJkdrG1/img.png&quot; data-alt=&quot;출처: 항해플러스 발제 자료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnAQ7o/btsPLIv5Kae/KsiJrSOoe0eJ0V2YJkdrG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnAQ7o%2FbtsPLIv5Kae%2FKsiJrSOoe0eJ0V2YJkdrG1%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;800&quot; height=&quot;584&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: 항해플러스 발제 자료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;DB Lock&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동시성 제어를 위한 여러가지 방법이 있지만, 이번 챕터에서는 DB Lock을 이용해 동시성을 제어하기로 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;락의 종류에는 2가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pessimistic Lock (비관적 락)&lt;/li&gt;
&lt;li&gt;Optimistic Lock (낙관적 락) &lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 378px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;Pessimistic Lock (비관적 락)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Optimistic&amp;nbsp;Lock&amp;nbsp;(낙관적&amp;nbsp;락)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;핵심 아이디어&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&amp;ldquo;&lt;b&gt;충돌(경합)이 자주 난다&lt;/b&gt;&amp;rdquo; &amp;rarr; 먼저 잠그고 진행&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&amp;ldquo;&lt;b&gt;충돌(경합)이 드물다&lt;/b&gt;&amp;rdquo; &amp;rarr; 일단 진행, 끝에 충돌 검사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;동작 방식&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;트랜잭션 시작 시점에 DB 레벨 잠금(행/범위)을 획득하고 유지(FOR UPDATE 등)&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;레코드에 버전(또는 타임스탬프) 필드 유지 &amp;rarr; 커밋 전 비교 불일치 시 예외로 재시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;충돌 처리&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;대기(락 대기 시간/타임아웃) 또는 데드락&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;예외(OptimisticLockException) 발생 &amp;rarr; 애플리케이션 레벨 재시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;데드락 위험&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;상대적으로 높음(락 순서/범위 설계 필요)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;거의 없음(락이 아니라 버전 비교)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;스루풋/지연&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;경쟁 심하면 지연 증가, 스루풋 저하&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;경쟁이 드물면 고스루풋, 충돌 시 재시도 비용 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;읽기 처리&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;공유/배타 락으로 인해 읽기도 지연될 수 있음&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;읽기는 자유로움(스냅샷 일관성 전제)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;롤백/복구&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;DB가 동기적으로 직렬화에 가깝게 보장&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;애플리케이션이 재시도&amp;middot;머지 전략 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;적용에 유리한 경우&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;재고 차감&amp;middot;좌석/쿠폰 선착순 등 &amp;ldquo;유일성/수량 소진&amp;rdquo; 같이 충돌이 빈번&amp;middot;치명적일 때&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;대다수 조회+소수 업데이트, 폼 편집, 비즈니스가 약간의 재시도 허용할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;트랜잭션 길이 민감도&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;매우 민감(짧게 유지 필수)&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;상대적으로 덜 민감(그래도 짧을수록 좋음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;운영상 주의&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;락 범위 최소화, 인덱스 설계, 타임아웃/재시도, 락 순서 고정&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;버전 컬럼 필수, 재시도 백오프&amp;middot;최대 시도 수, 사용성 메시지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;모니터링 지표&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;락 대기 시간, 데드락 수, InnoDB row lock waits&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;OptimisticLockException 비율, 재시도 성공률&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;DB 의존성&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;강한 DB 락 기능에 의존&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;상대적으로 DB 독립적(ORM 버전 필드)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동시성 제어 보고서&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/bskjp1004/hhplus-ecommerce-server/blob/STEP09-STEP10/docs/report/STEP09/Concurrency_Controll_Report.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/bskjp1004/hhplus-ecommerce-server/blob/STEP09-STEP10/docs/report/STEP09/Concurrency_Controll_Report.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754837979843&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;hhplus-ecommerce-server/docs/report/STEP09/Concurrency_Controll_Report.md at STEP09-STEP10 &amp;middot; bskjp1004/hhplus-ecommerce-server&quot; data-og-description=&quot;Contribute to bskjp1004/hhplus-ecommerce-server development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/bskjp1004/hhplus-ecommerce-server/blob/STEP09-STEP10/docs/report/STEP09/Concurrency_Controll_Report.md&quot; data-og-url=&quot;https://github.com/bskjp1004/hhplus-ecommerce-server/blob/STEP09-STEP10/docs/report/STEP09/Concurrency_Controll_Report.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Yt7RV/hyZvxD2Bc0/dSNS3oQVkoujBJTqKIfTb1/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216,https://scrap.kakaocdn.net/dn/cCLzT4/hyZuIlEq2m/Wm8iD3Qlj45CYq4YKssJCK/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216&quot;&gt;&lt;a href=&quot;https://github.com/bskjp1004/hhplus-ecommerce-server/blob/STEP09-STEP10/docs/report/STEP09/Concurrency_Controll_Report.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/bskjp1004/hhplus-ecommerce-server/blob/STEP09-STEP10/docs/report/STEP09/Concurrency_Controll_Report.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Yt7RV/hyZvxD2Bc0/dSNS3oQVkoujBJTqKIfTb1/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216,https://scrap.kakaocdn.net/dn/cCLzT4/hyZuIlEq2m/Wm8iD3Qlj45CYq4YKssJCK/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216');&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;hhplus-ecommerce-server/docs/report/STEP09/Concurrency_Controll_Report.md at STEP09-STEP10 &amp;middot; bskjp1004/hhplus-ecommerce-server&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to bskjp1004/hhplus-ecommerce-server 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;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;KPT회고&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Keep&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;미구현되어있던 선착순 쿠폰발급 기능을 구현했음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;비관적 락으로만 우선 다 구현하고, 낙관적 락은 안하려했는데.. 결국 했음&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;최근 3일간 인기상품 조회 기능도 마저 구현하려했는데, 이번 주차 과제에 더 집중하기 위해 구현하지 못한게 아쉬움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;낙관적 락을 적용하면서, 기존에 잘 작동하던 통합테스트들이 에러나는 걸 겪으며, 괜히 낙관적 락을 적용하려 시도했나.. 세상이 무너짐을 겪었음.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Try&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;미구현된 기능 구현&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <category>항해99</category>
      <category>항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/21</guid>
      <comments>https://jeanin-blog.tistory.com/21#entry21comment</comments>
      <pubDate>Mon, 11 Aug 2025 00:00:29 +0900</pubDate>
    </item>
    <item>
      <title>[WIL] JPA와 테스트 컨테이너</title>
      <link>https://jeanin-blog.tistory.com/20</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 교육 과정 수강중 작성된 학습노트입니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;❔WIL(Weekly I Learned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;JPA(Java Persistence API)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z8sIG/btsPIfFPnWa/ZjGegi2xk5yKqDKkMfExLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z8sIG/btsPIfFPnWa/ZjGegi2xk5yKqDKkMfExLk/img.png&quot; data-alt=&quot;출처: https://albertprofe.dev/springboot/boot-concepts-jpa-2.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z8sIG/btsPIfFPnWa/ZjGegi2xk5yKqDKkMfExLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz8sIG%2FbtsPIfFPnWa%2FZjGegi2xk5yKqDKkMfExLk%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;800&quot; height=&quot;388&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://albertprofe.dev/springboot/boot-concepts-jpa-2.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;JPA는 Java 진영에서 사용되는 &lt;b&gt;ORM 기술 표준&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;JPA는 인터페이스이기 때문에 실제적으로 JPA를 구현한 ORM 프레임워크는 Hibernate, OpenJPA, EclipseLink 등이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;ORM 프레임워크 중에서는 Hibernate가 많이 사용된다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt; ORM이란?&lt;br /&gt;데이터베이스의 테이블과 객체를 연결하여, SQL 쿼리 없이 객체 조작만으로 데이터베이스 작업을 가능하게 하는 기술.&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;JPA - Hibernate 사용 방법&lt;/h3&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;b&gt;의존성 추가(Gradle 기준)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`build.gradle.kts`파일에 아래 의존성 추가&lt;/p&gt;
&lt;pre id=&quot;code_1754321495907&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	// Spring Data JPA
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    // MySQL
	runtimeOnly 'mysql:mysql-connector-java'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터베이스 연결 설정&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;`src/main/resources/application.yml`에 접속 정보 작성&lt;/p&gt;
&lt;pre id=&quot;code_1754321954407&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  # DB 접속정보는 {} 안의 정보를 수정
  datasource:
    url: jdbc:mysql://localhost:3306/{your_db_name}?serverTimezone=Asia/Seoul&amp;amp;characterEncoding=UTF-8
    username: {db_user}
    password: {db_password}
  jpa:
    hibernate:
      ddl-auto: update      # 개발 중: update, 배포 전엔 validate 또는 none 권장
    show-sql: true          # 실행되는 SQL 로그 확인
    properties:
      hibernate:
        format_sql: true&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;엔티티 클래스 정의&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754322060109&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;orders&quot;)
public class OrderJpaEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private Long userId;

    private LocalDateTime orderedAt;

    // ... getter, setter, 생성자 등
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리포지토리(Repository)&amp;nbsp;인터페이스&amp;nbsp;생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754322275954&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface OrderRepository extends JpaRepository&amp;lt;OrderJpaEntity, Long&amp;gt; {
    // 메서드 이름으로 쿼리 자동 생성
    List&amp;lt;OrderJpaEntity&amp;gt; findAllByUserId(Long userId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서비스 레이어에서 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754322319190&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class OrderService {

    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Transactional(readOnly = true)
    public List&amp;lt;OrderJpaEntity&amp;gt; getOrdersByUser(Long userId) {
        return orderRepository.findAllByUserId(userId);
    }

    @Transactional
    public OrderJpaEntity createOrder(OrderJpaEntity order) {
        order.setOrderedAt(LocalDateTime.now());
        return orderRepository.save(order); // JpaRepository에서 제공되는 save 메서드
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;테스트 컨테이너(Testcontainers)&lt;/h2&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;JPA를 이용해 인프라스트럭처 계층의 코드를 작성하고, 실제 DB 범위까지의 통합테스트를 작성하기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합테스트에 유용하게 쓰이는 도구로 테스트 컨테이너가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8wC0o/btsPF7hW1py/5tyn5JEyh3bSDLPimrnvuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8wC0o/btsPF7hW1py/5tyn5JEyh3bSDLPimrnvuK/img.png&quot; data-alt=&quot;출처: https://www.devkuma.com/docs/testcontainers/overview/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8wC0o/btsPF7hW1py/5tyn5JEyh3bSDLPimrnvuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8wC0o%2FbtsPF7hW1py%2F5tyn5JEyh3bSDLPimrnvuK%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;800&quot; height=&quot;493&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://www.devkuma.com/docs/testcontainers/overview/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테스트 컨테이너(Testcontainers)는 도커 컨테이너를 이용해 실제 서비스와의 통합 테스트를 수행하기 위한 경량화된 컨테이너 라이브러리이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테스트 환경을 구성하기 귀찮은데, 테스트 컨테이너가 도커를 이용해 DB 환경을 만들고 테스트가 종료되면 도커 컨테이너가 종료되니 테스트 데이터도 같이 지워진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;테스트&amp;nbsp;컨테이너(Testcontainers)&amp;nbsp;사용 방법&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 데스크탑 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.docker.com/products/docker-desktop/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754323873875&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;Docker Desktop: The #1 Containerization Tool for Developers | Docker&quot; data-og-description=&quot;Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.&quot; data-og-host=&quot;www.docker.com&quot; data-og-source-url=&quot;https://www.docker.com/products/docker-desktop/&quot; data-og-url=&quot;https://www.docker.com/products/docker-desktop/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cbb0rh/hyZrAnRxwR/abqWhL8KlJhIzEUumIfgXK/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/bLnrWo/hyZvoF1YGS/S1D6zkVTkY5S4XFJVLrEj0/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/dRqW0L/hyZvjEIVMB/h80gOPn1aljY24yNIJRv61/img.png?width=2596&amp;amp;height=1629&amp;amp;face=0_0_2596_1629&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.docker.com/products/docker-desktop/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cbb0rh/hyZrAnRxwR/abqWhL8KlJhIzEUumIfgXK/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/bLnrWo/hyZvoF1YGS/S1D6zkVTkY5S4XFJVLrEj0/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/dRqW0L/hyZvjEIVMB/h80gOPn1aljY24yNIJRv61/img.png?width=2596&amp;amp;height=1629&amp;amp;face=0_0_2596_1629');&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;Docker Desktop: The #1 Containerization Tool for Developers | Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의존성 추가(Gradle 기준)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;`build.gradle.kts`파일에 아래 의존성 추가&lt;/p&gt;
&lt;pre id=&quot;code_1754323681819&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    // Spring Boot Test (JUnit5 포함)
    testImplementation(&quot;org.springframework.boot:spring-boot-starter-test&quot;)
    // Spring Boot 와 Testcontainers 통합 스타터
    testImplementation(&quot;org.springframework.boot:spring-boot-testcontainers&quot;)
    // JUnit5용 Testcontainers 어댑터
    testImplementation(&quot;org.testcontainers:junit-jupiter&quot;)
    // MySQL 컨테이너 모듈
    testImplementation(&quot;org.testcontainers:mysql&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;통합 테스트 코드 작성&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;`src/test/` 패키지 하위에 테스트 클래스 생성 및 테스트 코드 작성&lt;/p&gt;
&lt;pre id=&quot;code_1754324049669&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@SpringBootTest
class OrderIntegrationTest {

    // 1) MySQL 컨테이너 선언 (static이면 테스트 클래스 당 한 번만 띄웁니다)
    @Container
    static MySQLContainer&amp;lt;?&amp;gt; mysql =
        new MySQLContainer&amp;lt;&amp;gt;(&quot;mysql:8.0&quot;)
            .withDatabaseName(&quot;testdb&quot;)
            .withUsername(&quot;test&quot;)
            .withPassword(&quot;test&quot;);

    // 2) 컨테이너 프로퍼티를 Spring 환경에 등록
    @DynamicPropertySource
    static void registerMysql(DynamicPropertyRegistry registry) {
        registry.add(&quot;spring.datasource.url&quot;,      mysql::getJdbcUrl);
        registry.add(&quot;spring.datasource.username&quot;, mysql::getUsername);
        registry.add(&quot;spring.datasource.password&quot;, mysql::getPassword);
    }

    @Autowired
    private OrderFacade orderFacade;

    @Test
    void placeOrder_success() {
        // given
        CreateOrderCommand cmd = /* ... */;
        // when
        OrderResult result = orderFacade.placeOrderWithPayment(cmd);
        // then
        assertThat(result).isNotNull();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;KPT회고&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Keep&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;기본 과제만 하고 포기하고 싶었지만 심화 과제까지 도전하였음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;3주차 피드백을 반영해 개선한 점. 커밋을 잘 해보려 노력하였음.. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JpaEntity를 작성하고 연관 관계에 있는 도메인끼리 맵핑하는 작업이 어려웠음&lt;/li&gt;
&lt;li&gt;핵심 기능 이외의 요구사항에 작성된 기능도 개발하고 싶었지만, 시간이 부족했음&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Try&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;4주차 피드백 반영하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;핵심 기능 이외의 기능 1개 추가 개발하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/20</guid>
      <comments>https://jeanin-blog.tistory.com/20#entry20comment</comments>
      <pubDate>Tue, 5 Aug 2025 01:17:12 +0900</pubDate>
    </item>
    <item>
      <title>[WIL] 레이어드 아키텍처(Layered Architecture)</title>
      <link>https://jeanin-blog.tistory.com/19</link>
      <description>&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 교육 과정 수강중 작성된 학습노트입니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;❔WIL(Weekly I Learned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;레이어드&amp;nbsp;아키텍처(Layered&amp;nbsp;Architecture)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQbQrm/btsPBjhW2A8/sxVkhYA9ufX5W0WCNIBy11/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQbQrm/btsPBjhW2A8/sxVkhYA9ufX5W0WCNIBy11/img.webp&quot; data-alt=&quot;출처: https://www.codzgarage.com/blog/software-architecture-patterns/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQbQrm/btsPBjhW2A8/sxVkhYA9ufX5W0WCNIBy11/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQbQrm%2FbtsPBjhW2A8%2FsxVkhYA9ufX5W0WCNIBy11%2Fimg.webp&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;800&quot; height=&quot;700&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://www.codzgarage.com/blog/software-architecture-patterns/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;레이어드 아키텍처는 가장 일반적인 아키텍처로 대부분의 Java EE 애플리케이션에서 &lt;b&gt;사실상 표준으로 사용&lt;/b&gt;되고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아키텍처 이름 그대로 n개의 계층이 레이어 처럼 구성되어 있는 패턴이며, 보통 4가지의 계층으로 구성된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; 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;h3 data-ke-size=&quot;size23&quot;&gt;각 계층의 역할과 책임&lt;/h3&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;바로 &lt;b&gt;구성 요소간의 관심사를 분리&lt;/b&gt;하기 위해서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로는 객체지향설계 5원칙(SOLID) 중에서 S에 해당하는 '단일 책임 원칙(Single&amp;nbsp;Responsibility&amp;nbsp;Principle)'과 비슷하다고 생각했다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyXmfw/btsPz0KNeGa/kGD0bWciJvKv4LkiPDJR8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyXmfw/btsPz0KNeGa/kGD0bWciJvKv4LkiPDJR8k/img.png&quot; data-alt=&quot;출처: https://www.oreilly.com/content/software-architecture-patterns/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyXmfw/btsPz0KNeGa/kGD0bWciJvKv4LkiPDJR8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyXmfw%2FbtsPz0KNeGa%2FkGD0bWciJvKv4LkiPDJR8k%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;800&quot; height=&quot;598&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://www.oreilly.com/content/software-architecture-patterns/&lt;/figcaption&gt;
&lt;/figure&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;&lt;b&gt;Presentation Layer(프레젠테이션 계층)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;사용자 인터페이스 및 브라우저/클라이언트와의 통신 로직을 담당하는 계층&lt;/li&gt;
&lt;li&gt;e.g. `Controller`, `DTO(View 용)`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Business(Application) Layer(비즈니스 계층)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;핵심 비즈니스 로직을 수행하는 계층&lt;/li&gt;
&lt;li&gt;e.g. `Service`, `Domain Model`, `DTO(내부 전달용)`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Persistence Layer(영속성 계층)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;데이터베이스와의 상호작용을 전담하는 계층&lt;/li&gt;
&lt;li&gt;e.g. `Repository`, `ORM`, `JpaEntity`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Database Layer(데이터베이스 계층)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;실제 데이터 저장소의 역할을 하는 계층, 애플리케이션 내부 계층이 아닌 외부 인프라 자원으로 간주됨&lt;/li&gt;
&lt;li&gt;e.f. `MySQL`, `PostgreSQL`, `MongoDB`, `Redis`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;상위 계층 &amp;rarr; 하위 계층, 의존은 단방향이어야 한다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QiJun/btsPCKeCU4h/xfEQTvbnaDRCfhZFtdq2kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QiJun/btsPCKeCU4h/xfEQTvbnaDRCfhZFtdq2kk/img.png&quot; data-alt=&quot;출처: https://www.oreilly.com/content/software-architecture-patterns/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QiJun/btsPCKeCU4h/xfEQTvbnaDRCfhZFtdq2kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQiJun%2FbtsPCKeCU4h%2FxfEQTvbnaDRCfhZFtdq2kk%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;800&quot; height=&quot;545&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://www.oreilly.com/content/software-architecture-patterns/&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;레이어드 아키텍처의 핵심 개념 중 하나는 '&lt;b&gt;폐쇄형 계층&lt;/b&gt;'이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이게 무슨 의미일까?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요청 흐름이 계층을 하나씩 통과해야 하며, 특정 계층을 점프하듯 건너뛸 수 없다는 뜻이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드 구조 상 &lt;b&gt;상위 계층&lt;/b&gt;(`Conttoller`)은 &lt;b&gt;하위 계층&lt;/b&gt;(`Service`)을 &lt;b&gt;참조&lt;/b&gt;할 수 있지만, 하위 계층은 그럴 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;`Controller`에서 `Service`를 건너뛰고 `Repository`를 직접 호출 ➡️ 계층 건너뛰기 금지❌&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;` &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Repository&lt;/span&gt; `에서 ` &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Service&lt;/span&gt; `를 참조 ➡️ 하위 계층이 상위 계층을 역방향 참조 금지❌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Layered Architecture with DIP, 레이어드 아키텍처의 진화&lt;/h3&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: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전통적인 레이어드 아키텍처는 변화에 취약한 의존성 구조를 가지고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 `Service`가 `JpaRepository 구현체`를 직접 참조해서 개발된 코드가 있다면,&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ORM을 JPA가 아닌 다른 도구로 바꾸려면 유지보수 비용과 시간이 많이 소요된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 보완하기 위해&amp;nbsp;&lt;b&gt;DIP(Dependency&amp;nbsp;Inversion&amp;nbsp;Principle,&amp;nbsp;의존성&amp;nbsp;역전&amp;nbsp;원칙)&amp;nbsp;&lt;/b&gt;개념이 도입되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rtBG0/btsPCMpZWbT/jbOtxTL4Oigx1LALgShiNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rtBG0/btsPCMpZWbT/jbOtxTL4Oigx1LALgShiNk/img.png&quot; data-alt=&quot;1차 출처: 《 도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지 》, 최범균&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rtBG0/btsPCMpZWbT/jbOtxTL4Oigx1LALgShiNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrtBG0%2FbtsPCMpZWbT%2FjbOtxTL4Oigx1LALgShiNk%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;800&quot; height=&quot;451&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1차 출처: 《 도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지 》, 최범균&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;2차 출처: &lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://velog.io/@gehwan96/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EC%9E%A5.-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;도메인 주도 개발 시작하기 - 2 (아키텍처 개요 - 2장)&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DIP를 지키기 위해 도메인 영역(비즈니스 계층)에서 `OrderRepository`와 같이 인터페이스를 작성하고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인프라스트럭처 영역(영속성 계층)에서 `JpaOrderRepository`로 구현한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 `OrderService`가 `JpaOrderRepository` 구현체를 직접 의존하지 않고, 인터페이스를 통해 의존하기 때문에 JPA가 아닌 다른 ORM으로 바뀐다면 구현체만 바꾸면 되기때문에 변화에 능동적을 대처할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;아키텍처를 직접 적용해보자&lt;/h2&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;655&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vCkfV/btsPBHiCz9E/A5qXN7QBWCJNhnSaClCZFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vCkfV/btsPBHiCz9E/A5qXN7QBWCJNhnSaClCZFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vCkfV/btsPBHiCz9E/A5qXN7QBWCJNhnSaClCZFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvCkfV%2FbtsPBHiCz9E%2FA5qXN7QBWCJNhnSaClCZFk%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;800&quot; height=&quot;655&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;655&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9rar/btsPAE1rZ99/XZmifUUJsiuigDhce4oCfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9rar/btsPAE1rZ99/XZmifUUJsiuigDhce4oCfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9rar/btsPAE1rZ99/XZmifUUJsiuigDhce4oCfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9rar%2FbtsPAE1rZ99%2FXZmifUUJsiuigDhce4oCfK%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;800&quot; height=&quot;335&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 패키지 구조&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3rSlm/btsPBeVrBXJ/9ofAbBCYDo7ymcoYQuoN30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3rSlm/btsPBeVrBXJ/9ofAbBCYDo7ymcoYQuoN30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3rSlm/btsPBeVrBXJ/9ofAbBCYDo7ymcoYQuoN30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3rSlm%2FbtsPBeVrBXJ%2F9ofAbBCYDo7ymcoYQuoN30%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;363&quot; height=&quot;357&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;KPT회고&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Keep&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;최대한 아키텍처의 의존성 역전 원칙을 지키기 위해 노력한 점&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;1주차&amp;nbsp;TDD&amp;nbsp;피드백을&amp;nbsp;공부해&amp;nbsp;`AssertJ`를&amp;nbsp;활용해&amp;nbsp;가독성을&amp;nbsp;높이려&amp;nbsp;했음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;요청 및 응답 DTO를 활용해 아키텍처 계층 경계에서 요청하고 반환받는 구조를 짜는게 어려웠음 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;예외 처리도 계층별로 분리하고 싶었으나 제대로 작성한 것 같지는 않음 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;2주차 설계와 많이 달라지지는 않았지만 시퀀스 다이어그램이나 API 명세서가 아주 약간 지켜지지 않은 것 같음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Try&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;요청 및 응답 DTO 활용 방안에 대해 더 심도있게 공부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;설계 수정해서 반영하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;레퍼런스&lt;/h2&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;a href=&quot;https://www.oreilly.com/content/software-architecture-patterns/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.oreilly.com/content/software-architecture-patterns/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1753630693336&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;Software architecture patterns&quot; data-og-description=&quot;Take a deep dive into several common software architecture patterns&quot; data-og-host=&quot;www.oreilly.com&quot; data-og-source-url=&quot;https://www.oreilly.com/content/software-architecture-patterns/&quot; data-og-url=&quot;https://www.oreilly.com/content/software-architecture-patterns/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oVYg7/hyZns4yMaP/glVYwvPk7Bg8amVZnRU4w0/img.jpg?width=1024&amp;amp;height=622&amp;amp;face=0_0_1024_622,https://scrap.kakaocdn.net/dn/cKsf2B/hyZnrYUZiX/Qwc3Bng3q4Ux9YNM1M2Gv1/img.jpg?width=1024&amp;amp;height=622&amp;amp;face=0_0_1024_622,https://scrap.kakaocdn.net/dn/bHLozO/hyZnxdMKU3/czoiM3KPPXo6HqKT14LhOk/img.png?width=1354&amp;amp;height=923&amp;amp;face=0_0_1354_923&quot;&gt;&lt;a href=&quot;https://www.oreilly.com/content/software-architecture-patterns/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.oreilly.com/content/software-architecture-patterns/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oVYg7/hyZns4yMaP/glVYwvPk7Bg8amVZnRU4w0/img.jpg?width=1024&amp;amp;height=622&amp;amp;face=0_0_1024_622,https://scrap.kakaocdn.net/dn/cKsf2B/hyZnrYUZiX/Qwc3Bng3q4Ux9YNM1M2Gv1/img.jpg?width=1024&amp;amp;height=622&amp;amp;face=0_0_1024_622,https://scrap.kakaocdn.net/dn/bHLozO/hyZnxdMKU3/czoiM3KPPXo6HqKT14LhOk/img.png?width=1354&amp;amp;height=923&amp;amp;face=0_0_1354_923');&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;Software architecture patterns&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Take a deep dive into several common software architecture patterns&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.oreilly.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@gehwan96/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EC%9E%A5.-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@gehwan96/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EC%9E%A5.-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1753630735191&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;도메인 주도 개발 시작하기 - 2 (아키텍처 개요 - 2장)&quot; data-og-description=&quot;1. 네 개의 영역 웹 애플리케이션의 표현 영역 HTTP 요청을 응용 역역이 필요로 하는 형식으로 변환해 전달 응용 영역의 응답을 HTTP 응답으로 변환하여 전송 웹 애플리케이션의 응용 영역 표현 영&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@gehwan96/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EC%9E%A5.-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94&quot; data-og-url=&quot;https://velog.io/@gehwan96/도메인-주도-개발-시작하기-2장.-아키텍처-개요&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bpjaoP/hyZm8ZwoCn/KX33Vkh0OIAfs4QDdivQn0/img.png?width=1880&amp;amp;height=574&amp;amp;face=0_0_1880_574,https://scrap.kakaocdn.net/dn/cahjQB/hyZm9RGdXG/POxQ6u6tpzJKwnnsYAgPUk/img.png?width=1880&amp;amp;height=574&amp;amp;face=0_0_1880_574,https://scrap.kakaocdn.net/dn/t98Yi/hyZm9qzFhJ/k0t0fJ2UwYKaCJWXZX7Cdk/img.png?width=1846&amp;amp;height=1164&amp;amp;face=0_0_1846_1164&quot;&gt;&lt;a href=&quot;https://velog.io/@gehwan96/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EC%9E%A5.-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@gehwan96/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EC%9E%A5.-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bpjaoP/hyZm8ZwoCn/KX33Vkh0OIAfs4QDdivQn0/img.png?width=1880&amp;amp;height=574&amp;amp;face=0_0_1880_574,https://scrap.kakaocdn.net/dn/cahjQB/hyZm9RGdXG/POxQ6u6tpzJKwnnsYAgPUk/img.png?width=1880&amp;amp;height=574&amp;amp;face=0_0_1880_574,https://scrap.kakaocdn.net/dn/t98Yi/hyZm9qzFhJ/k0t0fJ2UwYKaCJWXZX7Cdk/img.png?width=1846&amp;amp;height=1164&amp;amp;face=0_0_1846_1164');&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;도메인 주도 개발 시작하기 - 2 (아키텍처 개요 - 2장)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 네 개의 영역 웹 애플리케이션의 표현 영역 HTTP 요청을 응용 역역이 필요로 하는 형식으로 변환해 전달 응용 영역의 응답을 HTTP 응답으로 변환하여 전송 웹 애플리케이션의 응용 영역 표현 영&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <category>Layered Architecture with DIP</category>
      <category>레이어드 아키텍처</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/19</guid>
      <comments>https://jeanin-blog.tistory.com/19#entry19comment</comments>
      <pubDate>Mon, 28 Jul 2025 00:52:25 +0900</pubDate>
    </item>
    <item>
      <title>[WIL] 개발의 시작 단계: 설계</title>
      <link>https://jeanin-blog.tistory.com/18</link>
      <description>&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 교육 과정 수강중 작성된 학습노트입니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;❔WIL(Weekly I Learned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; 분석 및 설계 단계의 산출물들&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구사항 명세서: 프로젝트의 기능과 특징, 설계 및 제한사항들이 담긴 기술 문서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능: 시스템이나 소프트웨어가 수행해야 하는 특정 기능 또는 작업을 정의&lt;/li&gt;
&lt;li&gt;비기능: 시스템의 품질, 성능, 보안, 유지보수 등과 관련된 요구사항을 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&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;시퀀스 다이어그램: 시스템&amp;nbsp;내에서&amp;nbsp;객체(또는&amp;nbsp;서비스)들이&amp;nbsp;어떻게&amp;nbsp;상호&amp;nbsp;작용하는지를&amp;nbsp;시간&amp;nbsp;순서대로&amp;nbsp;표현하는&amp;nbsp;다이어그램&lt;/li&gt;
&lt;li&gt;클래스 다이어그램: 시스템에서&amp;nbsp;사용되는&amp;nbsp;객체(Class)들의&amp;nbsp;구조와&amp;nbsp;관계를&amp;nbsp;표현하는&amp;nbsp;다이어그램&lt;/li&gt;
&lt;li&gt;상태 다이어그램: 객체 또는 프로세스가 특정 이벤트에 따라 상태가 어떻게 변하는지를 표현하는 다이어그램&lt;/li&gt;
&lt;li&gt;ERD 다이어그램: 데이터베이스의&amp;nbsp;테이블&amp;nbsp;간&amp;nbsp;관계를&amp;nbsp;표현하는&amp;nbsp;다이어그램&lt;/li&gt;
&lt;li&gt;API 명세서: API&amp;nbsp;명세서는&amp;nbsp;API의&amp;nbsp;동작&amp;nbsp;방식,&amp;nbsp;엔드포인트,&amp;nbsp;요청&amp;nbsp;및&amp;nbsp;응답&amp;nbsp;구조,&amp;nbsp;인증&amp;nbsp;방식&amp;nbsp;등을&amp;nbsp;설명하는&amp;nbsp;문서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;요구사항 명세서&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;주어진 과제를 기반으로 요구사항 명세서를 작성했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기능보다 비기능 요구사항 작성하는 게 더 어려웠던 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;1133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uXMR5/btsPp6b4fqt/8bM5lmsUGkoDOVYJaRC1nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uXMR5/btsPp6b4fqt/8bM5lmsUGkoDOVYJaRC1nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uXMR5/btsPp6b4fqt/8bM5lmsUGkoDOVYJaRC1nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuXMR5%2FbtsPp6b4fqt%2F8bM5lmsUGkoDOVYJaRC1nk%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;670&quot; height=&quot;1133&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;1133&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;시퀀스 다이어그램&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;중요하다고 생각되는 기능 3가지 정도만 시퀀스 다이어그램을 작성했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;잔액 충전&lt;/li&gt;
&lt;li&gt;주문/결제&lt;/li&gt;
&lt;li&gt;쿠폰 발급&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;b&gt;주문/결제&lt;/b&gt;에 대한 시퀀스 다이어그램이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HOKz6/btsPp2AMd6g/K2rOkqSo7r198kzdwe9Pz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HOKz6/btsPp2AMd6g/K2rOkqSo7r198kzdwe9Pz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HOKz6/btsPp2AMd6g/K2rOkqSo7r198kzdwe9Pz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHOKz6%2FbtsPp2AMd6g%2FK2rOkqSo7r198kzdwe9Pz0%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;800&quot; height=&quot;608&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;ERD 다이어그램&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심이라고 생각되는 테이블만 작성했고, `주문` 테이블의 `user_id`는 실제로 DB 생성 시 `FK`를 설정 안하고, JPA에서만 관계를 설정해주려고 생각중이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTlKlm/btsPp7vf9fo/8qnDYyFyLHvsuvX2Yqq3x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTlKlm/btsPp7vf9fo/8qnDYyFyLHvsuvX2Yqq3x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTlKlm/btsPp7vf9fo/8qnDYyFyLHvsuvX2Yqq3x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTlKlm%2FbtsPp7vf9fo%2F8qnDYyFyLHvsuvX2Yqq3x0%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;800&quot; height=&quot;433&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;API 명세서&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;`yaml` 파일에 API 명세를 작성한 뒤 Swagger UI로 깔끔하게 볼 수 있도록 했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Swagger가 뭔지는 알고 있었는데, 이번에 처음 활용해봤다.&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;1065&quot; data-origin-height=&quot;7753&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBE20C/btsPp0b3Tf4/VIpg5WaPrSxM5EpqFYLkvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBE20C/btsPp0b3Tf4/VIpg5WaPrSxM5EpqFYLkvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBE20C/btsPp0b3Tf4/VIpg5WaPrSxM5EpqFYLkvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBE20C%2FbtsPp0b3Tf4%2FVIpg5WaPrSxM5EpqFYLkvK%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;1065&quot; height=&quot;7753&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;7753&quot;/&gt;&lt;/span&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;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Keep&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과제를&amp;nbsp;제출했음&lt;/li&gt;
&lt;li&gt;브랜치를 나누어 작업하였음&lt;/li&gt;
&lt;li&gt;PR을 알맞게 작성하였음&lt;/li&gt;
&lt;li&gt;커밋을 키워드를 붙여 세세하게 작성하였음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 다이어그램, 상태 다이어그램도 학습을 위해 작성하려했으나, 시간 부족으로 하지 못한게 아쉬움&lt;/li&gt;
&lt;li&gt;Mock API 작성에서 yaml 파일을 직접 작성하는 것이 아닌, 컨트롤러를 만들었다면 E2E 테스트까지 할 수 있었을 것 같은데, 못해서 아쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Try&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;및&amp;nbsp;설계서&amp;nbsp;작성을&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;심도있게&amp;nbsp;공부할&amp;nbsp;것&lt;/li&gt;
&lt;li&gt;RESTful&amp;nbsp;API와&amp;nbsp;관련된&amp;nbsp;용어&amp;nbsp;및&amp;nbsp;개념을&amp;nbsp;더&amp;nbsp;심화&amp;nbsp;학습&amp;nbsp;할&amp;nbsp;것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/18</guid>
      <comments>https://jeanin-blog.tistory.com/18#entry18comment</comments>
      <pubDate>Sun, 20 Jul 2025 21:25:05 +0900</pubDate>
    </item>
    <item>
      <title>[WIL] TDD? 들어는 봤는데, 직접 작성해 보라구?!</title>
      <link>https://jeanin-blog.tistory.com/16</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 교육 과정 수강중 작성된 학습노트입니다.&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;&lt;span data-token-index=&quot;0&quot;&gt;❔WIL(Weekly I Learned)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;테스트 코드 작성 첫 도전기&amp;nbsp;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;'TDD' 들어는 봤지만, 직접 작성해본 경험이 없어서 굉장히 당황했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하루종일 작성한 코드를 몽땅 삭제하고, PR 작성에 서툴러 과제 제출 20분 전에 후다닥 수정하고..&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우당탕탕 TDD 학습 주간이었다. &lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TDD에 대하여&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmBiRb/btsPgx13xuT/EEKj76cqQ1ZgmLtYV5eoKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmBiRb/btsPgx13xuT/EEKj76cqQ1ZgmLtYV5eoKk/img.png&quot; data-alt=&quot;출처: https://namu.wiki/w/%ED%85%8C%EC%8A%A4%ED%8A%B8%20%EC%A3%BC%EB%8F%84%20%EA%B0%9C%EB%B0%9C&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmBiRb/btsPgx13xuT/EEKj76cqQ1ZgmLtYV5eoKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmBiRb%2FbtsPgx13xuT%2FEEKj76cqQ1ZgmLtYV5eoKk%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;800&quot; height=&quot;531&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://namu.wiki/w/%ED%85%8C%EC%8A%A4%ED%8A%B8%20%EC%A3%BC%EB%8F%84%20%EA%B0%9C%EB%B0%9C&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;TDD(테스트 주도 개발)&lt;/b&gt;는 요구사항의 기능을 개발하기 전에 &lt;b&gt;실제적인 구현보다 테스트 코드를 먼저 작성&lt;/b&gt;하여, 작은 단위의 테스트 케이스를 통과하는 코드를 작성하는 것을 반복하여 기능을 구현하는 소프트웨어 방법론이다. 2002년 켄트 벡이 자신의 책을 출간하게 되면서 알려졌다고 한다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Write a failing test&lt;/span&gt;: 실패하는 테스트 코드를 먼저 작성한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Make the test pass&lt;/span&gt;: 테스트 코드를 성공시키기 위한 실제 코드를 작성한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;Refactor&lt;/span&gt;: 중복 코드 제거, 일반화 등의 리팩토링을 수행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트의 범위는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트의 범위는 어떻게 정할까? &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가짜 데이터베이스를 만들어 포인트 충전에 대한 서비스에 대해서만?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 오는 요청부터 서비스와 실제 데이터베이스를 거쳐 서버가 클라이언트에게 응답을 보내주는 것 까지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트에 범위에 따라 다음과 같이 &lt;b&gt;단위 테스트&lt;/b&gt;, &lt;b&gt;통합 테스트&lt;/b&gt;, &lt;b&gt;E2E 테스트&lt;/b&gt;로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(처음 입문하면 이 범위가 어디까지 인가에 대해 많이 고민하고, 어려운 부분이다.)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 84.186%; height: 63px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.6926%; height: 21px;&quot;&gt;&lt;b&gt;Unit Testing(단위 테스트)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.4934%; height: 21px;&quot;&gt;함수,&amp;nbsp;메서드,&amp;nbsp;클래스&amp;nbsp;단위로&amp;nbsp;개별&amp;nbsp;동작이&amp;nbsp;정확한지&amp;nbsp;확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.6926%; height: 21px;&quot;&gt;&lt;b&gt;Integration Testing(통합 테스트)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.4934%; height: 21px;&quot;&gt;여러&amp;nbsp;모듈이나&amp;nbsp;서비스&amp;nbsp;간의&amp;nbsp;상호작용이&amp;nbsp;잘&amp;nbsp;이루어지는지&amp;nbsp;확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.6926%; height: 21px;&quot;&gt;&lt;b&gt;End-to-End Testing(E2E 테스트)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.4934%; height: 21px;&quot;&gt;시스템을&amp;nbsp;실제&amp;nbsp;사용자처럼&amp;nbsp;전체적으로&amp;nbsp;테스트&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;h4 data-ke-size=&quot;size20&quot;&gt;테스트 피라미드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드를 빠르고 안정적이며 유지 관리가 쉽게 하려면 &lt;b&gt;테스트 피라미드(Test Pyramid)&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&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-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qOpiO/btsPhjWnvXI/JmdgGSBhNHLKPiY7ZfSKk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qOpiO/btsPhjWnvXI/JmdgGSBhNHLKPiY7ZfSKk1/img.png&quot; data-alt=&quot;출처: https://circleci.com/blog/testing-pyramid/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qOpiO/btsPhjWnvXI/JmdgGSBhNHLKPiY7ZfSKk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqOpiO%2FbtsPhjWnvXI%2FJmdgGSBhNHLKPiY7ZfSKk1%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;800&quot; height=&quot;465&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://circleci.com/blog/testing-pyramid/&lt;/figcaption&gt;
&lt;/figure&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;&lt;b&gt;End-to-end testing(E2E 테스트)&lt;/b&gt;: 피라미드의 꼭대기에 위치한 E2E 테스트는 사용자 인터페이스, API, 데이터베이스 및 기타 서비스를 포함하여 최종 사용자가 경험하는 방식대로 전체 애플리케이션을 검사하는 것을 포함합니다. 가장 복잡하고 실행 시간이 가장 깁니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Integration testing(통합 테스트)&lt;/b&gt;: 피라미드의 중간 부분을 차지하는 이 테스트는 서로 다른 모듈이나 외부 시스템 간의 상호작용을 확인합니다. 단위 테스트에 비해 개수가 적고, 비용이 많이 들며, 실행 속도가 느립니다.&lt;br /&gt;통합 테스트는 단위 테스트보다 빈도를 줄여야 하며, 일반적으로 개발 주기의 주요 시점, 예를 들어 중요한 기능이나 변경 사항이 프로젝트의 주요 브랜치에 통합된 후에 실행해야 합니다. 이렇게 하면 세부적이고 세부적인 단위 테스트가 모든 작은 변경 사항에 대해 실행되는 반면, 통합 테스트는 주요 단계에서 시스템의 전반적인 일관성과 기능을 검증하여 개발 프로세스의 철저함과 효율성 사이에서 균형을 유지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Unit testing(단위 테스트)&lt;/b&gt;: 피라미드의 기반을 형성하는 이 테스트는 단일 기능과 작은 코드 단위에 초점을 맞춥니다. 일반적으로 개수가 많지만 작성하고 실행하는 데 시간이 오래 걸리지 않습니다. 단위 테스트는 자주 실행해야 하며, 이상적으로는 자동화된 지속적 통합 프로세스 의 일부로 실행해야 합니다 . 이렇게 하면 새로운 변경 사항이 기존 기능을 손상시키지 않는지 확인하고 개발 라이프사이클 전반에 걸쳐 높은 수준의 코드 품질을 유지할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⬇️ 테스트 피라미드 출처 ⬇️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777; text-align: center;&quot;&gt;&lt;a href=&quot;https://circleci.com/blog/testing-pyramid/&quot;&gt;https://circleci.com/blog/testing-pyramid/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1752409746167&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;The testing pyramid: Strategic software testing for Agile teams&quot; data-og-description=&quot;Enhance your development process with the testing pyramid. Learn tips for adding unit, integration, and E2E tests for improved reliability and quality.&quot; data-og-host=&quot;circleci.com&quot; data-og-source-url=&quot;https://circleci.com/blog/testing-pyramid/&quot; data-og-url=&quot;https://circleci.com/blog/testing-pyramid/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bAnNwh/hyZjfctMaT/lw2cpPnCmMNLSFD5tn6r6K/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/mRgui/hyZjsC1rDM/KbjxPcip0FZvGobsmFiAxK/img.png?width=1186&amp;amp;height=690&amp;amp;face=0_0_1186_690,https://scrap.kakaocdn.net/dn/LlRHJ/hyZjgoTW8X/KfCq2CkKjfEMzTwsAzzat1/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://circleci.com/blog/testing-pyramid/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://circleci.com/blog/testing-pyramid/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bAnNwh/hyZjfctMaT/lw2cpPnCmMNLSFD5tn6r6K/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/mRgui/hyZjsC1rDM/KbjxPcip0FZvGobsmFiAxK/img.png?width=1186&amp;amp;height=690&amp;amp;face=0_0_1186_690,https://scrap.kakaocdn.net/dn/LlRHJ/hyZjgoTW8X/KfCq2CkKjfEMzTwsAzzat1/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&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;The testing pyramid: Strategic software testing for Agile teams&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Enhance your development process with the testing pyramid. Learn tips for adding unit, integration, and E2E tests for improved reliability and quality.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;circleci.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Keep&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과제 제출을 완료한 점, PASS를 받은 점이 잘했음&lt;/li&gt;
&lt;li&gt;테스트 케이스를 여러 경우를 생각해보려는 시도가 좋았음(코치님 리뷰)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 계층에 대한 역할과 책임에 대한 고민을 많이 했지만, 아직 부족한 것 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;잔액이&amp;nbsp;증가되어야&amp;nbsp;하는&amp;nbsp;역할과&amp;nbsp;책임은&amp;nbsp;누구에게&amp;nbsp;있어야&amp;nbsp;할까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;예외 처리에 대한 고민이 부족했던 것 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`ApiControllerAdvice`에 더 다양한 에러 처리를 작성했으면 좋았을 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Git 커밋과 PR 작성에 서툴렀던 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Try&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Git 커밋을 기능별로 작성하기(깃 데스트탑 이용), PR을 STEP 별로 브랜치 생성해서 작성하기&lt;/li&gt;
&lt;li&gt;도메인에 역할과 책임을 고민해보기(도메인에 작성되어야 할 기능이 무엇인지)&lt;/li&gt;
&lt;li&gt;피드백을 반영해서 테스트 코드 수정해보기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <category>항해99</category>
      <category>항해플러스백엔드</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/16</guid>
      <comments>https://jeanin-blog.tistory.com/16#entry16comment</comments>
      <pubDate>Sun, 13 Jul 2025 23:07:49 +0900</pubDate>
    </item>
    <item>
      <title>[개발자 커리어 회고] 시작하는 마음</title>
      <link>https://jeanin-blog.tistory.com/15</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;지금까지 개발자 커리어 회고&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;심리학 전공을 그만두고 개발자의 길로 들어서다&lt;/h4&gt;
&lt;p data-end=&quot;237&quot; data-start=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;고등학생 때부터 목표했던 상담사의 꿈은 대학원 진학 실패와 취직 압박으로 잠시 접어두고, 새로운 진로 '개발자'를 꿈꾸게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;283&quot; data-start=&quot;239&quot; data-ke-size=&quot;size16&quot;&gt;개발자 부트캠프가 취업 연계를 해준다는 친구의 말에 교육과정에 참여하게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;378&quot; data-start=&quot;285&quot; data-ke-size=&quot;size16&quot;&gt;빅데이터 서비스 개발자 과정에 참여하면서 `Java`, `Python` 등의 프로그래밍 언어를 익히고, 새로운 지식들을 이해하고 머릿속에 집어넣기 바쁜 나날을 보냈다.&lt;/p&gt;
&lt;p data-end=&quot;436&quot; data-start=&quot;380&quot; data-ke-size=&quot;size14&quot;&gt;(그 당시 `for문`을 이용해 별 짓기 코드를 짜는 것을 어려워해서 부끄러웠던 기억이 있다.. )&lt;/p&gt;
&lt;p data-end=&quot;503&quot; data-start=&quot;438&quot; data-ke-size=&quot;size16&quot;&gt;크고 작은 여러 프로젝트를 진행하며, 팀원들과 토의하고 밤 10~11시까지 코딩하며 어떻게든 마무리하려 애를 썼었다.&lt;/p&gt;
&lt;p data-end=&quot;569&quot; data-start=&quot;505&quot; data-ke-size=&quot;size16&quot;&gt;6개월의 시간이 지나 과정을 무사히 수료했고, 첫 직장으로 MES 솔루션을 서비스하는 SI 회사에 입사하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개발자 새싹 을 틔우기까지&lt;/h4&gt;
&lt;p data-end=&quot;650&quot; data-start=&quot;598&quot; data-ke-size=&quot;size16&quot;&gt;첫 직장에 입사하고 `C#`, `MSSQL` 등의 기술을 배우며 개발자로의 커리어를 시작했다.&lt;/p&gt;
&lt;p data-end=&quot;721&quot; data-start=&quot;652&quot; data-ke-size=&quot;size16&quot;&gt;`.NET Framework`, 유료 라이브러리 `DevExpress` 등을 이용해 웹 기반 MES 솔루션 개발을 담당했다.&lt;/p&gt;
&lt;p data-end=&quot;789&quot; data-start=&quot;723&quot; data-ke-size=&quot;size14&quot;&gt;(지금 생각하면 어이없는 일이지만, 입사일에는 사수가 비주얼 스튜디오 IDE에서 디버깅하는 방법부터 가르쳐 주었다.. )&lt;/p&gt;
&lt;p data-end=&quot;866&quot; data-start=&quot;791&quot; data-ke-size=&quot;size16&quot;&gt;입사할 당시만 해도 회사가 2년밖에 안 돼서 외주 맡겨 받은 프로젝트를 복사하는 방식으로 개발했었고, 하드코딩과 레거시 코드가 많았다.&lt;/p&gt;
&lt;p data-end=&quot;951&quot; data-start=&quot;868&quot; data-ke-size=&quot;size16&quot;&gt;특정 `Controller`, `Service` 기능을 만드는 것보다는 DB에 데이터를 넣고 수정하기 위해 쿼리문을 작성하는 일이 주로 대부분이었다.&lt;/p&gt;
&lt;p data-end=&quot;1093&quot; data-start=&quot;953&quot; data-ke-size=&quot;size16&quot;&gt;사용자가 누르는 버튼과 직접 연결된 비하인드 코드에 기능을 작성하면 되는 방식인 `.NET Framework WebForms`를 이용한 웹 페이지 개발은 개발자가 빠르게 이해가 가능하고 바로 코드 작성이 용이하지만, 자잘한 코드 실수가 많이 발생했다.&lt;/p&gt;
&lt;p data-end=&quot;1205&quot; data-start=&quot;1095&quot; data-ke-size=&quot;size14&quot;&gt;(`.NET Framework WebForms`: 버튼 클릭, 텍스트 입력, 페이지 로딩 등의 사용자 인터랙션을 이벤트로 처리하고, 개발자는 해당 이벤트에 대한 핸들러 메서드만 작성하면 되는 구조)&lt;/p&gt;
&lt;p data-end=&quot;1335&quot; data-start=&quot;1207&quot; data-ke-size=&quot;size16&quot;&gt;이에 코딩 천재 동료 개발자가 `Blazor`를 도입해 DI(의존성 주입), Entity, DTO, Controller, Service 등의 개념을 이용해 유지보수가 용이하고 메서드 간의 강결합을 느슨하게 할 수 있도록 개선했다.&lt;/p&gt;
&lt;p data-end=&quot;1335&quot; data-start=&quot;1207&quot; data-ke-size=&quot;size14&quot;&gt;(그 당시 2년 차 개발자였지만, 당연한 개념을 이제야 알게 된 것 같아 창피했다.)&lt;/p&gt;
&lt;p data-end=&quot;1335&quot; data-start=&quot;1207&quot; data-ke-size=&quot;size16&quot;&gt;새로 맡은 식품업 관련 솔루션은 바로 차세대 프레임워크를 가져다 개발했다.&lt;/p&gt;
&lt;p data-end=&quot;1335&quot; data-start=&quot;1207&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;h4 data-ke-size=&quot;size20&quot;&gt;활짝 피지못하고 아직 꽃봉오리, 성장이 더욱 고프다.. &lt;/h4&gt;
&lt;p data-end=&quot;1499&quot; data-start=&quot;1429&quot; data-ke-size=&quot;size16&quot;&gt;팀장 직책을 맡고, R&amp;amp;D 부서로 팀 이동이 되면서 개발 업무뿐만 아니라 인건비 사업 관리나 자잘한 회사 업무도 맡게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;1576&quot; data-start=&quot;1501&quot; data-ke-size=&quot;size16&quot;&gt;물론 노력과 열정만 있으면 이런 업무를 맡으면서 개발도 같이하는 것이 어렵지 않은 사람도 있겠지만,&lt;/p&gt;
&lt;p data-end=&quot;1576&quot; data-start=&quot;1501&quot; data-ke-size=&quot;size16&quot;&gt;나는 조금 과부하가 왔던 것 같다.&lt;/p&gt;
&lt;p data-end=&quot;1650&quot; data-start=&quot;1578&quot; data-ke-size=&quot;size16&quot;&gt;사업계획서 작성, 사업 관리, 개발 업무, 프로젝트 서버 배포, 교육 등&lt;/p&gt;
&lt;p data-end=&quot;1650&quot; data-start=&quot;1578&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트의 시작과 끝을 모두 해본 경험은 아주 귀중했다.&lt;/p&gt;
&lt;p data-end=&quot;1721&quot; data-start=&quot;1652&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이제는 전반적인 모든 업무를 할 줄 아는 것보다,&lt;/p&gt;
&lt;p data-end=&quot;1721&quot; data-start=&quot;1652&quot; data-ke-size=&quot;size16&quot;&gt;개발 스킬업과 직무를 더 첨예하게 갈고닦을 필요성을 절실히 느꼈다.&lt;/p&gt;
&lt;p data-end=&quot;1807&quot; data-start=&quot;1723&quot; data-ke-size=&quot;size16&quot;&gt;4년 10개월... 연차로 따지면 5년 차 개발자인데&lt;/p&gt;
&lt;p data-end=&quot;1807&quot; data-start=&quot;1723&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;항해 플러스 참여 계기 &lt;/span&gt;&lt;/h2&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;이력서와 포트폴리오를 작성하고 여러 대기업 채용공고에 지원했지만, 서류 단계 부터 탈락하기 일쑤였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(이력서를 몇십 개씩 지원한다는데, 아직은 열 손가락 안에 꼽을 횟수다..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'아, 내가 갖추지 못한 자질과 경험이 있다. 좀 더 전문적인 지식을 갈고 닦아야 겠다..'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기 저기 검색을 하다 항해 플러스 백엔드 교육 과정을 보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'딱 내 이야기인데?  '&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;향후 5년 뒤 커리어 방향성&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 커리어는 2가지 선택지를 고려 중이다.   &lt;br /&gt;  항해 플러스의 코치님들처럼 진짜 시니어 개발자가 되고 싶다.&lt;br /&gt;  같은 개발자 출신으로서 커뮤니케이션이 잘 통하는, 설계 짱 잘하는 기획자가 되고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;10주간의 목표&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기본 과제 제출 완료하기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;팀장 역할 충실히 이행하기(팀원들 중도 탈락 막기, 응원하기)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;교육 과정 완주하기&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;최종 목표 배지 &lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #953b34;&quot;&gt;브라운 배지&lt;/span&gt;&lt;/b&gt;(과제 80%이상 통과)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;(조건이 '제출'이 아니라 '통과'인 점에 주목해야한다.)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <category>항해99</category>
      <category>항해플러스 백엔드</category>
      <category>회고</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/15</guid>
      <comments>https://jeanin-blog.tistory.com/15#entry15comment</comments>
      <pubDate>Mon, 7 Jul 2025 01:52:17 +0900</pubDate>
    </item>
    <item>
      <title>[사전스터디/Java] Java 언어 강의 학습노트③</title>
      <link>https://jeanin-blog.tistory.com/13</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 과정의 일환인 개강 전 '사전스터디'의 학습노트입니다.&lt;br /&gt;⚠️개인이 학습한 것을 바탕으로 작성되어 부정확할 수 있으며, 오류가 있다면 알려주시면 감사하겠습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;강의 수강 목록&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Optional - null을 다루는 법&lt;/li&gt;
&lt;li&gt;컬렉션(Collection)&lt;/li&gt;
&lt;li&gt;제네릭(Generic)&lt;/li&gt;
&lt;li&gt;람다(Lambda)&lt;/li&gt;
&lt;li&gt;스트림(Steram)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Optional&amp;nbsp;-&amp;nbsp;null을&amp;nbsp;다루는&amp;nbsp;법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜&amp;nbsp;Optional이&amp;nbsp;필요한가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 null 체크 방법은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1751542831068&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String name = getUserName();
if (name != null) {
    System.out.println(name.length());
}&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;⚠️ 문제점: null 체크를 매번 해야 하고, 실수로 빼먹으면 `NullPointerException`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Optional 기본 사용법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Optional 객체 생성&lt;/p&gt;
&lt;pre id=&quot;code_1751543233716&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; name = Optional.of(&quot;ChatGPT&quot;); // 값이 반드시 있어야 함
Optional&amp;lt;String&amp;gt; empty = Optional.empty();       // 비어있는 Optional
Optional&amp;lt;String&amp;gt; nullable = Optional.ofNullable(null); // null도 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2.&amp;nbsp;값&amp;nbsp;꺼내기&lt;/p&gt;
&lt;pre id=&quot;code_1751543247504&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name.get();                    // 값이 없으면 NoSuchElementException 발생
name.orElse(&quot;기본값&quot;);         // 값이 없으면 기본값 리턴
name.orElseGet(() -&amp;gt; &quot;기본&quot;);  // 람다로 기본값 제공
name.orElseThrow();            // 값이 없으면 예외 발생 (기본적으로 NoSuchElementException)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3.&amp;nbsp;값이&amp;nbsp;있을&amp;nbsp;때만&amp;nbsp;처리&lt;/p&gt;
&lt;pre id=&quot;code_1751543258004&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name.ifPresent(value -&amp;gt; System.out.println(value)); // 값이 있으면 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;컬렉션&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;분류&lt;/td&gt;
&lt;td style=&quot;width: 12.907%;&quot;&gt;인터페이스&lt;/td&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;주요 구현 클래스&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 39.3024%;&quot;&gt;주요 메서드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;목록(List)&lt;/td&gt;
&lt;td style=&quot;width: 12.907%;&quot;&gt;`List&amp;lt;E&amp;gt;`&lt;/td&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;`ArrayList`, `LinkedList`, `Vector`&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;순서 있음, 중복 허용, 인덱스 접근 가능&lt;/td&gt;
&lt;td style=&quot;width: 39.3024%;&quot;&gt;`add`(추가), `get`(인덱스로 조회), `remove`(삭제), `size`(길이 확인), `contains`(포함 여부)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;집합(Set)&lt;/td&gt;
&lt;td style=&quot;width: 12.907%;&quot;&gt;`Set&amp;lt;E&amp;gt;`&lt;/td&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;`HashSet`, `LinkedHashSet`, `TreeSet`&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;순서 없음 (또는 정렬), 중복 불가&lt;/td&gt;
&lt;td style=&quot;width: 39.3024%;&quot;&gt;`add`(추가), `remove`(삭제), `contains`(포함 여부), `size`(길이 확인)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;맵(Map)&lt;/td&gt;
&lt;td style=&quot;width: 12.907%;&quot;&gt;`Map&amp;lt;K, V&amp;gt;`&lt;/td&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;`HashMap`, `LinkedHashMap`, `TreeMap`&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;키-값 쌍 저장, 키 중복 불가&lt;/td&gt;
&lt;td style=&quot;width: 39.3024%;&quot;&gt;`put`(추가/수정), `get`(값 조회), `remove`(삭제), `containsKey`(키 존재 여부), `keySet`(모든 키 조회)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;큐/스택&lt;/td&gt;
&lt;td style=&quot;width: 12.907%;&quot;&gt;`Queue&amp;lt;E&amp;gt;` / `Deque&amp;lt;E&amp;gt;`&lt;/td&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;`LinkedList`, `PriorityQueue`, `ArrayDeque`&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;선입선출(FIFO), 양방향 가능&lt;/td&gt;
&lt;td style=&quot;width: 39.3024%;&quot;&gt;`add`(추가), `offer`(추가), `poll`(꺼내고 삭제), `peek`(확인만), `isEmpty`(비었는지 확인)&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;h3 data-ke-size=&quot;size23&quot;&gt;제네릭(Generic)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊게 생각하지 않고 제네릭을 사용했지만, 아래의 장점이 있다는 사실을 명확하게 알게 되었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 재사용성&lt;/li&gt;
&lt;li&gt;타입 안정성(컴파일러가 자동으로 다운 캐스팅)&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;size18&quot;&gt;⚠️일반메서드와 제네릭 메서드 구분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 메서드와 제네릭 메서드의 동작 원리를 명확히 구분할 수 있게 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1751538784409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class GenericBox&amp;lt;T&amp;gt; {

    private T item;

    public GenericBox(T item) {
        this.item = item;
    }

    public T getItem() {
        return this.item;
    }

    // ⚠️ 이 메서드는 클래스의 타입 매개변수 &amp;lt;T&amp;gt;를 사용하는 일반 메서드입니다.
    public void printItem(T item) {
        System.out.println(item);
    }
    
    // ✅ 이 메서드는 독립적인 제네릭 타입 &amp;lt;S&amp;gt;를 정의한 제네릭 메서드입니다. 클래스의 &amp;lt;T&amp;gt;와는 관계가 없습니다.
    public &amp;lt;S&amp;gt; void printBoxItem(S item) { 
        System.out.println(item);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;람다(Lambda)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;람다식&lt;/b&gt;은 익명 클래스를 간결하게 표현하는 문법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1751542659656&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 익명클래스
Calculator calculator1 = new Calculator() {
		@Override
		public int sum(int a, int b) {
				return a + b;
		}
};

// ✅ 람다 표현식
Calculator calculator1 = (a, b) -&amp;gt; a + b;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스트림(Stream)&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;C# LINQ와 비슷한 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`stream()`, `map()`, `collect()`&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;/p&gt;
&lt;pre id=&quot;code_1751543831544&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Java
import java.util.Arrays;

public class StreamExample {
    public static void main(String[] args) {
        int[] numbers = { 1, 2, 3, 4, 5 };

        int sum = Arrays.stream(numbers)
            .filter(n -&amp;gt; n % 2 == 0)
            .map(n -&amp;gt; n * 10)
            .sum();

        System.out.println(sum); // 출력: 60
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1751543891204&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// C#
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] numbers = { 1, 2, 3, 4, 5 };

        int sum = numbers
            .Where(n =&amp;gt; n % 2 == 0)
            .Select(n =&amp;gt; n * 10)
            .Sum();

        Console.WriteLine(sum); // 출력: 60
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>자기계발/항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/13</guid>
      <comments>https://jeanin-blog.tistory.com/13#entry13comment</comments>
      <pubDate>Thu, 3 Jul 2025 20:59:13 +0900</pubDate>
    </item>
    <item>
      <title>[사전스터디/SprintBoot] 학습노트③ (회원가입, 로그인 API 구현, AWS 배포)</title>
      <link>https://jeanin-blog.tistory.com/12</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 과정의 일환인 개강 전 '사전스터디'의 학습노트입니다.&lt;br /&gt;⚠️개인이 학습한 것을 바탕으로 작성되어 부정확할 수 있으며, 오류가 있다면 알려주시면 감사하겠습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; ️3주차 진행&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여행으로 인한 미참석&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; 4주차 목표&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;회원가입 및 로그인 API 구현&lt;/li&gt;
&lt;li&gt;JWT 토큰 발행&lt;/li&gt;
&lt;li&gt;AWS EC2 배포&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회원가입 및 로그인 API 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`UserService`와 `UserController`를 구현했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjrZBr/btsO1ASs1zx/GR9MscVho4b4elEIXjgGO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjrZBr/btsO1ASs1zx/GR9MscVho4b4elEIXjgGO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjrZBr/btsO1ASs1zx/GR9MscVho4b4elEIXjgGO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjrZBr%2FbtsO1ASs1zx%2FGR9MscVho4b4elEIXjgGO0%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;900&quot; height=&quot;472&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;472&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;DTO는 `LoginRequestDto`, `LoginResponseDto` 로그인 요청과 응답&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`SignupRequestDto`, `SignupResponseDto` 회원가입 요청과 응답&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zrZGK/btsO15q7RRy/dPNUiOTkj5itCgdDbJkskk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zrZGK/btsO15q7RRy/dPNUiOTkj5itCgdDbJkskk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zrZGK/btsO15q7RRy/dPNUiOTkj5itCgdDbJkskk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzrZGK%2FbtsO15q7RRy%2FdPNUiOTkj5itCgdDbJkskk%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;900&quot; height=&quot;470&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JWT&amp;nbsp;토큰&amp;nbsp;발행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Security&lt;/b&gt;, &lt;b&gt;JWT&lt;/b&gt; 관련 디펜던시 추가&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 14.4186%;&quot;&gt;그룹 ID&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%;&quot;&gt;아티팩트 ID&lt;/td&gt;
&lt;td style=&quot;width: 14.6512%;&quot;&gt;버전&lt;/td&gt;
&lt;td style=&quot;width: 8.48829%;&quot;&gt;scope&lt;/td&gt;
&lt;td style=&quot;width: 28.3721%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;Spring Security&lt;/td&gt;
&lt;td style=&quot;width: 14.4186%;&quot;&gt;`org.springframework.boot`&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%;&quot;&gt;spring-boot-starter-security&lt;/td&gt;
&lt;td style=&quot;width: 14.6512%;&quot;&gt;(Spring Boot에 따라 자동 설정)&lt;/td&gt;
&lt;td style=&quot;width: 8.48829%;&quot;&gt;기본&lt;/td&gt;
&lt;td style=&quot;width: 28.3721%;&quot;&gt;Spring Security를 사용하기 위한 스타터. 인증/인가 처리, 필터 구성 등을 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;JJWT - API&lt;/td&gt;
&lt;td style=&quot;width: 14.4186%;&quot;&gt;`io.jsonwebtoken`&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%;&quot;&gt;jjwt-api&lt;/td&gt;
&lt;td style=&quot;width: 14.6512%;&quot;&gt;0.11.5&lt;/td&gt;
&lt;td style=&quot;width: 8.48829%;&quot;&gt;기본&lt;/td&gt;
&lt;td style=&quot;width: 28.3721%;&quot;&gt;JWT 토큰 생성/검증을 위한 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;JJWT - 구현체&lt;/td&gt;
&lt;td style=&quot;width: 14.4186%;&quot;&gt;`io.jsonwebtoken`&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%;&quot;&gt;jjwt-impl&lt;/td&gt;
&lt;td style=&quot;width: 14.6512%;&quot;&gt;0.11.5&lt;/td&gt;
&lt;td style=&quot;width: 8.48829%;&quot;&gt;runtime&lt;/td&gt;
&lt;td style=&quot;width: 28.3721%;&quot;&gt;JJWT API의 실제 동작 구현체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;JJWT - Jackson&lt;/td&gt;
&lt;td style=&quot;width: 14.4186%;&quot;&gt;`io.jsonwebtoken`&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%;&quot;&gt;jjwt-jackson&lt;/td&gt;
&lt;td style=&quot;width: 14.6512%;&quot;&gt;0.11.5&lt;/td&gt;
&lt;td style=&quot;width: 8.48829%;&quot;&gt;runtime&lt;/td&gt;
&lt;td style=&quot;width: 28.3721%;&quot;&gt;Jackson을 이용한 JSON 직렬화/역직렬화 지원 (JWT 파싱/생성 시 사용)&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;`pom.xml`에 디펜던시 추가&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;pre id=&quot;code_1751466226411&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;		&amp;lt;!-- Spring Boot Starter --&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-starter-security&amp;lt;/artifactId&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;!-- JJWT (JSON Web Token) --&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;io.jsonwebtoken&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;jjwt-api&amp;lt;/artifactId&amp;gt;
			&amp;lt;version&amp;gt;0.11.5&amp;lt;/version&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;io.jsonwebtoken&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;jjwt-impl&amp;lt;/artifactId&amp;gt;
			&amp;lt;version&amp;gt;0.11.5&amp;lt;/version&amp;gt;
			&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;io.jsonwebtoken&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;jjwt-jackson&amp;lt;/artifactId&amp;gt;
			&amp;lt;version&amp;gt;0.11.5&amp;lt;/version&amp;gt;
			&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
		&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`JwtUtil`, `JwtFilter`, `SecurityConfig`, `UserService` 작성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tRNAD/btsO1BjvTVj/JoYkebXAZ3WYhye4dYFJW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tRNAD/btsO1BjvTVj/JoYkebXAZ3WYhye4dYFJW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tRNAD/btsO1BjvTVj/JoYkebXAZ3WYhye4dYFJW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtRNAD%2FbtsO1BjvTVj%2FJoYkebXAZ3WYhye4dYFJW0%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;900&quot; height=&quot;466&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AWS EC2 배포&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS EC2 사이트 접속( &lt;a href=&quot;https://aws.amazon.com/ko/ec2/&quot;&gt;https://aws.amazon.com/ko/ec2/&lt;/a&gt; )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1751466702922&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;company&quot; data-og-title=&quot;아마존 클라우드 서버 호스팅 | Amazon Web Services&quot; data-og-description=&quot;Amazon Elastic Compute Cloud(Amazon EC2)는 750개가 넘는 인스턴스, 그리고 최신 프로세서, 스토리지, 네트워킹, 운영 체제 및 구매 모델의 옵션과 함께 워크로드의 요구 사항에 가장 잘 부합할 수 있도록 &quot; data-og-host=&quot;aws.amazon.com&quot; data-og-source-url=&quot;https://aws.amazon.com/ko/ec2/&quot; data-og-url=&quot;https://aws.amazon.com/ko/ec2/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cJ7LVP/hyZf3vV26A/6G1zDlE2l8qlgwvSrYNN5K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/1Thkw/hyZf1rlTZo/cNU2AJGrOvYrzoumrUTo7k/img.png?width=179&amp;amp;height=109&amp;amp;face=0_0_179_109&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/ec2/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aws.amazon.com/ko/ec2/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cJ7LVP/hyZf3vV26A/6G1zDlE2l8qlgwvSrYNN5K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/1Thkw/hyZf1rlTZo/cNU2AJGrOvYrzoumrUTo7k/img.png?width=179&amp;amp;height=109&amp;amp;face=0_0_179_109');&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;아마존 클라우드 서버 호스팅 | Amazon Web Services&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Amazon Elastic Compute Cloud(Amazon EC2)는 750개가 넘는 인스턴스, 그리고 최신 프로세서, 스토리지, 네트워킹, 운영 체제 및 구매 모델의 옵션과 함께 워크로드의 요구 사항에 가장 잘 부합할 수 있도록&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입 후 로그인 진행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 검색해서 콘솔에 접속&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo82zr/btsO1CbGdHx/S7yOVxdCtU72Jtaae2Q5YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo82zr/btsO1CbGdHx/S7yOVxdCtU72Jtaae2Q5YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo82zr/btsO1CbGdHx/S7yOVxdCtU72Jtaae2Q5YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo82zr%2FbtsO1CbGdHx%2FS7yOVxdCtU72Jtaae2Q5YK%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;900&quot; height=&quot;459&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⬇️ 인스턴스 생성 및 설정은 아래 블로그를 참고하였음 ⬇️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ttl-blog.tistory.com/1335&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ttl-blog.tistory.com/1335&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751466808099&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;[배포 및 인프라] 해보자 배포 [1] - AWS EC2 생성하고 접속하기&quot; data-og-description=&quot;  어려운 배포 배포는 참 어렵습니다. 저는 지금 정말 너무 어려워서 울 것 같아요. 하지만, 배포를 처음 해보는 모두가 다 저와 같은 감정일 것이라 생각합니다. 그래서 배포를 처음하는 제가,&quot; data-og-host=&quot;ttl-blog.tistory.com&quot; data-og-source-url=&quot;https://ttl-blog.tistory.com/1335&quot; data-og-url=&quot;https://ttl-blog.tistory.com/1335&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Ab2PY/hyZf1kA0T0/v24I81WuHjoK6TECG2Ibxk/img.jpg?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/VCA4F/hyZf3Jtbof/IkIijYUXf7AaekVgYUTo41/img.jpg?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/eBqYek/hyZfqShbdk/waMCTwMjCAT90HW8XxyZ70/img.png?width=667&amp;amp;height=838&amp;amp;face=0_0_667_838&quot;&gt;&lt;a href=&quot;https://ttl-blog.tistory.com/1335&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ttl-blog.tistory.com/1335&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Ab2PY/hyZf1kA0T0/v24I81WuHjoK6TECG2Ibxk/img.jpg?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/VCA4F/hyZf3Jtbof/IkIijYUXf7AaekVgYUTo41/img.jpg?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/eBqYek/hyZfqShbdk/waMCTwMjCAT90HW8XxyZ70/img.png?width=667&amp;amp;height=838&amp;amp;face=0_0_667_838');&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;[배포 및 인프라] 해보자 배포 [1] - AWS EC2 생성하고 접속하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  어려운 배포 배포는 참 어렵습니다. 저는 지금 정말 너무 어려워서 울 것 같아요. 하지만, 배포를 처음 해보는 모두가 다 저와 같은 감정일 것이라 생각합니다. 그래서 배포를 처음하는 제가,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ttl-blog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⬇️ EC2 배포를 위한 명령어&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;⬇️&lt;/span&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;size18&quot;&gt;내 로컬PC ➡️ EC2 &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;`db_backup.sql` 파일 전송하기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751467752639&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 내 로컬 PC(Window)에서 cmd 혹은 PowerShell에서 실행

&amp;lt;MySQL 백업 sql 파일 '문서'폴더 경로에 만들기&amp;gt;
cd &quot;C:\Program Files\MySQL\MySQL Server 8.0\bin&quot;
.\mysqldump.exe -u root -p [데이터베이스 이름] &amp;gt; &quot;$HOME\Documents\db_backup.sql&quot;

&amp;lt;백업 파일을 EC2로 전송&amp;gt;
scp -i [SSH 파일(.pem) 경로] db_backup.sql ubuntu@[EC 퍼블릭 IP 주소]:/home/ubuntu/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EC2 인스턴스 연결 후 AWS 서버(Ubuntu)에서 실행&lt;/p&gt;
&lt;pre id=&quot;code_1751467741140&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;MySQL 설치&amp;gt;
sudo apt update
sudo apt install mysql-server -y

&amp;lt;MySQL 서비스 시작 및 자동 시작 설정&amp;gt;
sudo systemctl enable mysql
sudo systemctl start mysql

&amp;lt;MySQL 복원&amp;gt;
// mysql 실행
sudo mysql

// root 계정 비밀번호 설정
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '[root 계정 비밀번호]';
FLUSH PRIVILEGES;
EXIT;

// 데이터베이스 생성
CREATE DATABASE [데이터베이스 명];
CREATE USER '[유저 아이디]'@'%' IDENTIFIED BY '[유저 비밀번호]';
GRANT ALL PRIVILEGES ON mydb.* TO '[유저 아이디]'@'%';
FLUSH PRIVILEGES;
EXIT;

&amp;lt;백업 복원&amp;gt;
mysql -u root -p [데이터베이스 명] &amp;lt; db_backup.sql

&amp;lt;DB 사용자 권한 부여&amp;gt;
GRANT ALL PRIVILEGES ON [데이터베이스 명].* TO '[유저 아이디]'@'%';
FLUSH PRIVILEGES;
EXIT;


&amp;lt;깃 설치&amp;gt;
sudo apt-get install git
git --version

&amp;lt;깃 SSH KEY 생성&amp;gt;
cd ~/.ssh
ssh-keygen -t rsa -C [github계정 메일]

&amp;lt;깃 SSH KEY 확인&amp;gt;
cat id_rsa.pub

&amp;lt;깃허브에 Key 붙여넣기&amp;gt;

&amp;lt;root 로 이동&amp;gt;
cd /root

&amp;lt;SDKMAN 설치&amp;gt;
// uzip, zip 패키지 설치
sudo apt update
sudo apt install unzip -y
sudo apt install zip -y

// SDKMAN 설치
curl -s &quot;https://get.sdkman.io&quot; | bash
source &quot;$HOME/.sdkman/bin/sdkman-init.sh&quot;

// 버전 확인
sdk version

&amp;lt;Java 24 설치 및 버전 확인&amp;gt;
sdk install java 24-open
sdk use java 24-open
java -version

&amp;lt;Maven 설치&amp;gt;
sudo apt update
sudo apt install maven -y

&amp;lt;깃 클론하기&amp;gt;
git clone [레포지토리 주소]
cd [레포지토리 이름]

&amp;lt;프로젝트 빌드&amp;gt;
mvn clean package // 오류나면 아래 명령어로
mvn clean package -DskipTests // 프로젝트 빌드-테스트 코드 오류 시 테스트 건너뛰고 빌드

&amp;lt;Spring Boot JAR 실행&amp;gt;
java -jar target/[릴리즈된 .jar 파일]&lt;/code&gt;&lt;/pre&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;EC2 서버에 스프링부트 배포 완료&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM30ah/btsO0gtUk7T/ZZiemMTeevGhxXa8nJRKK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM30ah/btsO0gtUk7T/ZZiemMTeevGhxXa8nJRKK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM30ah/btsO0gtUk7T/ZZiemMTeevGhxXa8nJRKK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM30ah%2FbtsO0gtUk7T%2FZZiemMTeevGhxXa8nJRKK0%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;430&quot; height=&quot;200&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⬇️ 스프링부트 스터디 깃허브 리포지토리 ⬇️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/bskjp1004/SpringBootStudy/tree/master&quot;&gt;https://github.com/bskjp1004/SpringBootStudy/tree/master&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751468550821&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 - bskjp1004/SpringBootStudy&quot; data-og-description=&quot;Contribute to bskjp1004/SpringBootStudy development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/bskjp1004/SpringBootStudy/tree/master&quot; data-og-url=&quot;https://github.com/bskjp1004/SpringBootStudy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nvhvy/hyZftnVzWq/fTn7XvRgYRn0BCKkk9PXzK/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216,https://scrap.kakaocdn.net/dn/cwCtvh/hyZf67gONj/JzvYJAH5FBRDk0MlHH20wk/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216&quot;&gt;&lt;a href=&quot;https://github.com/bskjp1004/SpringBootStudy/tree/master&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/bskjp1004/SpringBootStudy/tree/master&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nvhvy/hyZftnVzWq/fTn7XvRgYRn0BCKkk9PXzK/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216,https://scrap.kakaocdn.net/dn/cwCtvh/hyZf67gONj/JzvYJAH5FBRDk0MlHH20wk/img.png?width=1200&amp;amp;height=600&amp;amp;face=990_153_1048_216');&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 - bskjp1004/SpringBootStudy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to bskjp1004/SpringBootStudy 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;
&lt;p data-ke-size=&quot;size16&quot;&gt;⬇️ API 명세서 ⬇️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/11hKFllK5-pVJjpwVTErXQ-pZIITKsmeC9Ba8zuty1z8/edit?gid=0#gid=0&quot;&gt;https://docs.google.com/spreadsheets/d/11hKFllK5-pVJjpwVTErXQ-pZIITKsmeC9Ba8zuty1z8/edit?gid=0#gid=0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751468552870&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;[항해플러스] 스프링부트 스터디 API 명세서&quot; data-og-description=&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ기능URLMethodRequest HeaderRequestResponse전체 게시글 목록 조회 API/api/postsGETContent-Type: application/json없음[ { &amp;quot;id&amp;quot;: 1, &amp;quot;title&amp;quot;: &amp;quot;문의글&amp;quot;, &amp;quot;content&amp;quot;: &amp;quot;문의 내용&amp;quot;, &amp;quot;username&amp;quot;: &amp;quot;admin&amp;quot;, &amp;quot;createdAt&amp;quot;: null, &quot; data-og-host=&quot;docs.google.com&quot; data-og-source-url=&quot;https://docs.google.com/spreadsheets/d/11hKFllK5-pVJjpwVTErXQ-pZIITKsmeC9Ba8zuty1z8/edit?gid=0#gid=0&quot; data-og-url=&quot;https://docs.google.com/spreadsheets/d/11hKFllK5-pVJjpwVTErXQ-pZIITKsmeC9Ba8zuty1z8/edit?gid=0&amp;amp;usp=embed_facebook&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3waeJ/hyZfv0mS6D/4Iq3FMl2ppjH9lnCFLn141/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/11hKFllK5-pVJjpwVTErXQ-pZIITKsmeC9Ba8zuty1z8/edit?gid=0#gid=0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.google.com/spreadsheets/d/11hKFllK5-pVJjpwVTErXQ-pZIITKsmeC9Ba8zuty1z8/edit?gid=0#gid=0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3waeJ/hyZfv0mS6D/4Iq3FMl2ppjH9lnCFLn141/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;[항해플러스] 스프링부트 스터디 API 명세서&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ABCDEFGHIJKLMNOPQRSTUVWXYZ기능URLMethodRequest HeaderRequestResponse전체 게시글 목록 조회 API/api/postsGETContent-Type: application/json없음[ { &quot;id&quot;: 1, &quot;title&quot;: &quot;문의글&quot;, &quot;content&quot;: &quot;문의 내용&quot;, &quot;username&quot;: &quot;admin&quot;, &quot;createdAt&quot;: null,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.google.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>자기계발/항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/12</guid>
      <comments>https://jeanin-blog.tistory.com/12#entry12comment</comments>
      <pubDate>Wed, 2 Jul 2025 23:59:30 +0900</pubDate>
    </item>
    <item>
      <title>[사전스터디/Java] Java 언어 강의 학습노트②</title>
      <link>https://jeanin-blog.tistory.com/11</link>
      <description>&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; 본 글은 항해플러스 백엔드 9기 과정의 일환인 개강 전 '사전스터디'의 학습노트입니다.&lt;br /&gt;⚠️개인이 학습한 것을 바탕으로 작성되어 부정확할 수 있으며, 오류가 있다면 알려주시면 감사하겠습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;강의 수강 목록&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향 PART 2 - 상속&lt;/li&gt;
&lt;li&gt;객체지향 PART 3 - 추상화&lt;/li&gt;
&lt;li&gt;객체지향 PART 4 - 다형성&lt;/li&gt;
&lt;li&gt;예외(Exception)과&amp;nbsp;예외처리(try-catch)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pQDeh/btsOZf0Qopt/VBn0y411FUzBjmdSmA69qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pQDeh/btsOZf0Qopt/VBn0y411FUzBjmdSmA69qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pQDeh/btsOZf0Qopt/VBn0y411FUzBjmdSmA69qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpQDeh%2FbtsOZf0Qopt%2FVBn0y411FUzBjmdSmA69qK%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;900&quot; height=&quot;369&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&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;`extends` 키워드를 자식 클래스에 사용해서 상속받음&lt;/p&gt;
&lt;pre id=&quot;code_1751267986022&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 부모
public class Parent {
    public String familyName = &quot;스파르탄&quot;;
    public int honor = 10;

    public void introduceFamily() {
        System.out.println(&quot;우리 &quot; + this.familyName + &quot; 가문은 대대로 명성을 이어온...&quot;);
    }
}

// 자식
class Child extends Parent {
}&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;`super` 키워드는 부모클래스의 멤버에 접근할 때 사용하는 키워드&lt;/p&gt;
&lt;pre id=&quot;code_1751268182917&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 부모
public class Parent {
    public Parent() {} // 부모 생성자
}

// 자식
public class Child extends Parent {

		...

    public Child() {
        super(); // (1)부모클래스 생성자를 먼저 호출
        // 추가로직 작성
    }
}&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;`@Override` 키워드를 통해 부모 클래스의 메서드를 재정의 할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1751268263093&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 부모
public class Parent {

    // 기존 기능
    public void introduceFamily() {
        System.out.println(&quot;우리 &quot; + familyName + &quot; 가문은 대대로 명성을 이어온 가문입니다.&quot;);
    }
}

// 자식
class Child extends Parent {
		...
    
    @Override
    void introduceFamily() { // 자식클래스에서 재정의
        System.out.println(&quot;오버라이드&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추상클래스와 인터페이스 비교&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 118px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 43.7209%;&quot;&gt;상황&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 28.1396%;&quot;&gt;추상클래스(abstract class)&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 28.0232%;&quot;&gt;인터페이스(interface)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 43.7209%;&quot;&gt;공통 속성과 구현 코드도 물려주고 싶다&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;✅ 적합&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.0232%;&quot;&gt;❌ 부적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 43.7209%;&quot;&gt;오직 기능의 약속만 필요하다&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;❌ 불필요&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.0232%;&quot;&gt;✅ 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 43.7209%;&quot;&gt;상태(필드)와 생성자를 가질 수 있어야 한다&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;✅ 가능&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.0232%;&quot;&gt;❌ 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 43.7209%;&quot;&gt;여러 타입을 구현해야 한다&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;❌ 단일만&lt;br /&gt;`extends`&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.0232%;&quot;&gt;✅ 다중 구현 가능&lt;br /&gt;`implements`&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1751269385574&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 추상 클래스
class A { }
class B { }

class C extends A, B { } // ❌ 컴파일 오류 발생!

// 인터페이스
interface Flyable { void fly(); }
interface Swimmable { void swim(); }

class Duck implements Flyable, Swimmable {
    public void fly() { System.out.println(&quot;오리가 날아요&quot;); }
    public void swim() { System.out.println(&quot;오리가 헤엄쳐요&quot;); }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#과 Java 비교&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;C#에서는 자식클래스를 구현할때 `extends`, `implements`와 같은 키워드 없이 `:`을 사용했었음&lt;/p&gt;
&lt;pre id=&quot;code_1751270189514&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Java
class Dog extends Animal implements Pet { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1751270204492&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// C#
class Dog : Animal, IPet { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추상화&lt;/h3&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;추상화의 계층적 특징을 활용해 유지보수가 용이한 코드를 작성할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1751268657445&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;생명체
  |
동물
  |
고양이&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다형성&lt;/h3&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;pre id=&quot;code_1751268974245&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;   생명체
     |
    동물
     |
고양이/강아지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예외처리&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;Exception (Checked Exception)&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&gt;RuntimeException (Unchecked Exception)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;상위 클래스&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;`java.lang.Exception`&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&gt;`java.lang.RuntimeException` (&amp;rarr; Exception의 하위)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;예외 처리&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;&lt;b&gt;반드시 처리&lt;/b&gt;해야 함 (try-catch 또는 throws)&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&gt;&lt;b&gt;선택적으로 처리&lt;/b&gt; 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;컴파일 시 검사&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;✅ (컴파일러가 예외 처리 여부 검사)&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&gt;❌ (컴파일러 검사 없음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;대표 예외&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;`IOException`, `SQLException`, `ParseException` 등&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&gt;`NullPointerException`, `ArrayIndexOutOfBoundsException`, `IllegalArgumentException` 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;주로 발생 상황&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;외부 환경 관련 예외 (파일, DB, 네트워크 등)&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&gt;프로그래밍 로직 실수, 버그&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.2558%;&quot;&gt;실무 처리 방식&lt;/td&gt;
&lt;td style=&quot;width: 39.0699%;&quot;&gt;반드시 처리 (`try-catch` 또는 `throws`)&lt;/td&gt;
&lt;td style=&quot;width: 42.5581%;&quot;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;❔상속 클래스와 인터페이스를 공부하면서 두 키워드의 차이에 대해 더 궁금해서 상속과 컴포지션의 개념에 대해 더 알아보았다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;상속과 컴포지션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상속&lt;/b&gt;은 &lt;b&gt;강한 결합&lt;/b&gt;을 만들며, 변경에 취약&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포지션&lt;/b&gt;은 객체 간의 &lt;b&gt;약한 연결로 유연&lt;/b&gt;하고 유지보수가 용이&lt;/li&gt;
&lt;li&gt;그래서 요즘 트렌드는 &quot;상속보다는 컴포지션을 활용하라(Prefer composition over inheritance)&quot;&lt;/li&gt;
&lt;li&gt;다만 GUI 프레임워크과 같은 &quot;is-a&quot; 관계가 뚜렷하고 안정적일 때 상속이 효과적임&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 139px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot;&gt;상속&amp;nbsp;(Inheritance)&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot;&gt;컴포지션 (Composition)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;구조&lt;/td&gt;
&lt;td&gt;is-a 관계&lt;/td&gt;
&lt;td&gt;has-a 관계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;부모 클래스의 기능을 자식 클래스가 물려받음&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;필요한 기능을 객체로 주입하여 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;유연성&lt;/td&gt;
&lt;td&gt;낮음 (고정된 계층 구조)&lt;/td&gt;
&lt;td&gt;높음 (동적으로 기능 변경 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;Dog extends Animal&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;Car has a Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;트렌드&lt;/td&gt;
&lt;td&gt;과도한 사용 지양&lt;/td&gt;
&lt;td&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;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;상속 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️문제점: 캐릭터가 두 가지 공격을 동시에 해야 한다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WarriorArcher 같은 새로운 클래스를 계속 만들어야 함 &amp;rarr; 클래스 폭발&lt;/p&gt;
&lt;pre id=&quot;code_1751272725960&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;abstract class Character {
    abstract void attack();
}

class Warrior extends Character {
    @Override
    void attack() {
        System.out.println(&quot;검으로 공격!&quot;);
    }
}

class Archer extends Character {
    @Override
    void attack() {
        System.out.println(&quot;화살로 공격!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;컴포지션 방식&lt;/h4&gt;
&lt;pre id=&quot;code_1751272725963&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;interface AttackStrategy {
    void attack();
}

class SwordAttack implements AttackStrategy {
    public void attack() {
        System.out.println(&quot;검으로 공격!&quot;);
    }
}

class BowAttack implements AttackStrategy {
    public void attack() {
        System.out.println(&quot;화살로 공격!&quot;);
    }
}

class Character {
    private AttackStrategy attackStrategy;

    public Character(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
    }

    public void performAttack() {
        attackStrategy.attack();
    }

    public void setAttackStrategy(AttackStrategy newStrategy) {
        this.attackStrategy = newStrategy;
    }
}

Character hero = new Character(new SwordAttack());
hero.performAttack();  // 검으로 공격!

hero.setAttackStrategy(new BowAttack());
hero.performAttack();  // 화살로 공격!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⬇️ 상속을 활용해 구현하려다 '리스코프 원칙'을 위반하는 문제에 대해 잘 설명한 블로그글 ⬇️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brownbears.tistory.com/579&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brownbears.tistory.com/579&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751273331689&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;[SOLID] 리스코프 원칙 법칙이란 (Liskov Substitution Principle, LSP)&quot; data-og-description=&quot;리스코프 치환 법칙은 SOLID 원칙에서 L에 해당하는 법칙입니다. 해당 법칙은 상위 타입의 객체를 하위 타입의 객체로 치환해도 동작에 문제가 없어야 합니다. 즉, B가 A의 자식일 때, A 타입을 사&quot; data-og-host=&quot;brownbears.tistory.com&quot; data-og-source-url=&quot;https://brownbears.tistory.com/579&quot; data-og-url=&quot;https://brownbears.tistory.com/579&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cf7893/hyZcq6Zo3E/66wla1GoSU1z0KzciPRi01/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cUQrn7/hyZbpUAyaB/O0kLdO9h4VKQtNaIDxwxb0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://brownbears.tistory.com/579&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://brownbears.tistory.com/579&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cf7893/hyZcq6Zo3E/66wla1GoSU1z0KzciPRi01/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cUQrn7/hyZbpUAyaB/O0kLdO9h4VKQtNaIDxwxb0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;[SOLID] 리스코프 원칙 법칙이란 (Liskov Substitution Principle, LSP)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리스코프 치환 법칙은 SOLID 원칙에서 L에 해당하는 법칙입니다. 해당 법칙은 상위 타입의 객체를 하위 타입의 객체로 치환해도 동작에 문제가 없어야 합니다. 즉, B가 A의 자식일 때, A 타입을 사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;brownbears.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기계발/항해플러스</category>
      <author>Jeanin</author>
      <guid isPermaLink="true">https://jeanin-blog.tistory.com/11</guid>
      <comments>https://jeanin-blog.tistory.com/11#entry11comment</comments>
      <pubDate>Mon, 30 Jun 2025 16:58:40 +0900</pubDate>
    </item>
  </channel>
</rss>