<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Love And Code</title>
    <link>https://johnmarc.tistory.com/</link>
    <description>Do Love! Write Code!</description>
    <language>ko</language>
    <pubDate>Wed, 8 Apr 2026 19:14:12 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>JohnMark</managingEditor>
    <image>
      <title>Love And Code</title>
      <url>https://tistory1.daumcdn.net/tistory/2194851/attach/366209f08bd44bc7b854f8958e9685e8</url>
      <link>https://johnmarc.tistory.com</link>
    </image>
    <item>
      <title>[Java] Enum 은 == 비교 아니면 equals() ?</title>
      <link>https://johnmarc.tistory.com/152</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;그 동안 써야지,, 하면서 계속 임시 저장되고만 있었던 게시글을 마무리 해보려고 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 회사 내부에서 CI/CD 파이프라인에 소나큐브를 도입하면서 코드 리뷰 또한 많이 활성화가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중 Enum 타입에 대한 비교에 대해서 짧게 코드리뷰를 한 내용이 있는데 내용을 축약해서 정리해본다.&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;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 내가 등록한 PR에 있던 코드 중 특정 코드가 대략 아래처럼 되어있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1650894423231&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public enum PaymentType
 REMITTANCE,
 ACCOUNT_TRANSFER,
 VISA_MASTER_CREDIT_CARD
 }

 public void someThingMethod(UserRequest userRequest){
     if(userRequest.getPaymentType() == PaymentType.REMITTANCE){
      // do something
      }
 }&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;이에 A 개발자 분이 다음과 같은 코드 코드 리뷰를 달아주셨다.&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;# A 개발자분의 답변&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650894671131&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PaymentType.REMITTANCE.equals(userRequest.getPaymentType())&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;enum에 대한 == 는 주소값들 비교합니다. 컴파일 시 타입 체크가 들어가기 때문에 다른 타입으로 비교하는 코드가 들어갔을 시 컴파일 단계에서 에러를 확인할 수 있습니다. 다만 NPE는 잡지 못합니다. &lt;br /&gt;equals는 오브젝트의 값들 비교하고, 내부적으로 값 비교 시 == 를 사용합니다. 컴파일 단계에서는 타입 체크를 하지 않습니다. 런타임 시 NPE는 잡을 수 있습니다.&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;이 코멘트를 보고, 생각에 빠지게 되었다. Enum Class의 경우 Constant 격의 Class로 Class Loader에 의해 메모리로 로딩될 시, Method Area로 할당되고 모든 Enum 객체는 동일한 객체를 공유해서 쓴다고 이해하고 있었다. 따라서 주소 값이 같기 때문에 == 비교를 하면 좀 더 빠르게 비교가 이루어질 것이라고 생각했고 equals의 경우 type check가 되지 않기 때문에 엉뚱한 값으로 비교 시 런타임 에러가 발생하기 때문에 == 비교가 맞다고 생각을 해왔다. 다만 validation 로직이 없었을 경우 == 비교 시 NPE가 발생할 수 있다는 사실은 간과하고 있었다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 코드를 다시 천천히 보던 중 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;해당 RequestDto에서 아래처럼 Controller Layer에서 Validation이 이루어지고 있었던 것을 확인했했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650894937214&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@Setter
public class UserRequest {
 @NotNull(message=&quot;payment type은 반드시 입력되어야 합니다&quot;)
 private PaymentType paymentType
}&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 값이 들어올 수 없기 때문에, service layer에서 enum type에 대한 비교문에서 NPE가 발생하지 않는다. 사실 == 이냐 equals냐에 대한 문제는, 간단한 수정 문제로 사실 해당 코드 리뷰를 받고 바로 적용할 수 있었지만 나는 사유가 궁금했다. &lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se8/jls8.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;오라클 문서&lt;/a&gt;에도 == 비교를 추천하고 있었고, &lt;a href=&quot;https://rules.sonarsource.com/java/RSPEC-4551&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;소나큐브에서도 == 비교로 룰&lt;/a&gt;이 정해져 있기 때문이다. 그래서 다음과 같이 궁금중을 담긴 반문을 해봤다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 나의 답변&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;말씀해주신 대로 해당 enum type 변수에 대해서는 controller 영역에서 request data를 dto class에 data binding 하는 과정에서 validation annotation으로 인해 null 여부가 체크되고 있습니다. 값에 대한 범위, null, 자료형, 패턴에 대한 검증은 controller 영역에서 이루어지고 부가적인 비즈니스 로직에 대한 검증은 service 영역에서 이루어지는 것으로 알고 있있어서, 저는 validatoin annotation으로 runtime 상태에서에 해당 enum 값의 NPE를 막고 비즈니스 로직에서는 == 비교를 통해 type mismatch와 NPE 문제를 잡고자 하였습니다.&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;이에 다시 A 개발자 분이 다음과 같이 답변해주셨다.&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;# A 개발자분의 답변&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;==는 주소 값을 비교 하기 때문에 NPE 발생하지 않습니다.&lt;br /&gt;enum은 class입니다. enum이 정의되지 않았다면 NPE가 발생할 수 있습니다. PaymentType.REMITTANCE.equals(userRequest.getPaymentType()) 로하면 NPE도 피할 수 있습니다. &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;나는 더욱더 흥미로워졌다.  그래서 간단하게 내 의견과 A 개발자분의 의견을 테스트 코드로 작성해보고 결과에 대해서 이야기를 나누어봤다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;# 나의 답변&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;생각해보니 == 는 주소 값을 비교하기 때문에 NPE가 발생하지 않는 게 맞네요! &amp;nbsp;&lt;br /&gt;PaymentType.REMITTANCE.equals(userRequest.getPaymentType()) 로하면 NPE도 피할 수는 있지만, 그 반대의 경우는 피할 수가 없습니다. &lt;br /&gt;== 연산자의 경우는 구문의 순서에 상관없이 NPE가 발생하지 않습니다. &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;1. equals의 순서가 바뀌는 경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;895&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qp2wx/btrAs9VfZlz/MDbw25IT83kaV0XMj9acIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qp2wx/btrAs9VfZlz/MDbw25IT83kaV0XMj9acIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qp2wx/btrAs9VfZlz/MDbw25IT83kaV0XMj9acIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqp2wx%2FbtrAs9VfZlz%2FMDbw25IT83kaV0XMj9acIk%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;1120&quot; height=&quot;895&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;895&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) == operator&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2xT0a/btrApkYh4fE/Vqqt0KYgKLA9AEYy6i8VIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2xT0a/btrApkYh4fE/Vqqt0KYgKLA9AEYy6i8VIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2xT0a/btrApkYh4fE/Vqqt0KYgKLA9AEYy6i8VIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2xT0a%2FbtrApkYh4fE%2FVqqt0KYgKLA9AEYy6i8VIK%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;1828&quot; height=&quot;519&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) equals method&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpMJTd/btrAp6S2ZmZ/zKwpDqHDKr2qLbqIapdZm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpMJTd/btrAp6S2ZmZ/zKwpDqHDKr2qLbqIapdZm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpMJTd/btrAp6S2ZmZ/zKwpDqHDKr2qLbqIapdZm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpMJTd%2FbtrAp6S2ZmZ%2FzKwpDqHDKr2qLbqIapdZm0%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;1267&quot; height=&quot;557&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;557&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;이 코멘트 이후로, 오프라인으로 한번 더 이야기를 나누어봤는데 A 개발자 분과 나 모두 간과한 사실이 있었다. Enum의 Equals메서드를 확인해보면 아래처럼 final 메서드로 구현되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BTTni/btrAp48GeBq/axjZFqO6JEzONshTfwZQ01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BTTni/btrAp48GeBq/axjZFqO6JEzONshTfwZQ01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BTTni/btrAp48GeBq/axjZFqO6JEzONshTfwZQ01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBTTni%2FbtrAp48GeBq%2FaxjZFqO6JEzONshTfwZQ01%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;1133&quot; height=&quot;354&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;354&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;어차피 equals 내부에서는 == 비교를 통해 주소 값을 비교하고 있었던 것이다. equals로 하던 == 비교를 하던 어차피 내부에선 == 비교를 한다. 그렇다면 == 비교를 적용한다면 컴파일 타임에는 타입 미스 매치를 잡고 런타임 시에는 NPE가 발생하지 않기에 두 마리 토끼를 모두 잡을 수 있다. 하지만 결국 equals로 모두 변경하기로 결정되었다. 이유는 명확하게 NPE 발생을 방지하고, 기존 서버 개발 구성원들 모두가 equals 비교 방식이 익숙했고 해당 방법에 동의를 했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 결정에 대해서는 딱히 섭섭하다 또는 부당하다는 생각은 전혀~~~ 들지 않는다. 팀의 기술 표준은 가능한 구성원 모두의 의견을 반영해서 결정되어야 하고(이래야 코드 리뷰할 때 서로 놓치지 않는다), 모두가 하나의 제품(서버 또는 프로그램)을 만들기 때문에 모두가 납득되는 방향으로 굴러가야 한다고 생각하기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 해당 문제는 매우 사소한 문제이므로 내 입장을 관철시킬 필요도 없다고 생각했다. 그저 이런 리뷰 절차가 너무나도 그리웠고 많은 것을 생각하고 찾아보게 된 경험이 변태스럽지만 행복하고 즐거웠다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3줄 요약:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. enum 비교 시 equals로 해야 하나요 == 비교를 해야 하나요!?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 둘 다 상관없습니다. equals 비교는 내부적으로 == 비교를 하고 있고, enum 상수가 앞으로 오게 비교하도록 해야 NPE 발생을 막습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 팀 내 표준을 정하고 따라가세요!&lt;/p&gt;</description>
      <category>Programming/Java</category>
      <category>Code Review</category>
      <category>enum</category>
      <category>enum == 비교</category>
      <category>enum equals</category>
      <category>enum 비교</category>
      <category>Java</category>
      <category>코드 리뷰</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/152</guid>
      <comments>https://johnmarc.tistory.com/152#entry152comment</comments>
      <pubDate>Mon, 25 Apr 2022 23:59:39 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크 기본] OSI 7계층 &amp;amp; TCP/IP Model</title>
      <link>https://johnmarc.tistory.com/151</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;회사 동료에게 네트워크 관련 질문을 설명을 하다가 OSI 7 계층과 TCP/IP 모델에 대한 내용을 말할 기회가 있었는데, 생각보다 까먹은 내용도 많고 제대로 정리되어있지 않아 다시 한번 정리를 해본다.&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;360&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCA0op/btrz3lwEg07/Ce12BLsgriVoypTuJM3AJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCA0op/btrz3lwEg07/Ce12BLsgriVoypTuJM3AJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCA0op/btrz3lwEg07/Ce12BLsgriVoypTuJM3AJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCA0op%2Fbtrz3lwEg07%2FCe12BLsgriVoypTuJM3AJ1%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;360&quot; height=&quot;374&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;374&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;OSI 7 계층은 네트워크 시스템 모델로 총 7개의 계층으로 나누어져있다. 이에 반해 OSI 5(TCP/IP 모델)은 5 계층으로 나누어져 있으며, 사실상 업계 표준은 TCP/IP 모델이다. 그 이유는 TCP/IP 모델이 제일 먼저 나와서 산업 표준으로 선택되었고 그 이후에 OSI 7 계층이 나왔기 때문이다.&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;b&gt;OSI 7 계층&lt;/b&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 143px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Layer Number&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Data Unit&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Device&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Protocol Example&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;L7&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Application&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Data, Message&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;Http, SMTP, FTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;L6&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Represntation&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Data, Message&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;JPEG, ASCII, MPEG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;L5&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Session&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Data, Message&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;SSH, TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;L4&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Transport&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Segement&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;게이트웨이, L4 스위치(IP&amp;amp;Port, LoadBalancing)&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;TCP, UDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;L3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Network&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Packet&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;스위치(IP), 라우터&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;IP, ICMP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;L2&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Data Link&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 19px; text-align: center;&quot;&gt;Frame&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 19px; text-align: center;&quot;&gt;스위치(Mack Address), 브릿지&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 19px; text-align: center;&quot;&gt;MAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 10px; text-align: center;&quot;&gt;&lt;b&gt;L1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 10px; text-align: center;&quot;&gt;Physical&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 10px; text-align: center;&quot;&gt;Bit, Signal&lt;/td&gt;
&lt;td style=&quot;width: 15.5648%; height: 10px; text-align: center;&quot;&gt;허브, 리피터&lt;/td&gt;
&lt;td style=&quot;width: 13.0066%; height: 10px; text-align: center;&quot;&gt;Ethernet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;L7 (7 계층) - 유저 인터페이스 제공 &lt;b&gt;-&amp;gt; Data 단위&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 영역인 어플리케이션 영역은 응용 프로세스를 사용하여 통신, 사용자에게 보이는 영역이며 직접 상호 작용하는 영역이다. 예시 애플리케이션으로는 브라우저, PC 응용 프로그램 등이 있고 예시 프로토콜으로는 http, smtp, ftp, pop 등이 있다.&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;&lt;b&gt;L6 (6 계층) - 데이터 변환 -&amp;gt; Data 단위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- L6 영역은 표현 영역은 서로 다른 통신 기기간의 데이터 교환을 위한 데이터의 변환과 데이터의 압축, 데이터의 암호화가 이루어지는 계층. 데이터 표현의 차이를 해결한다. 예시로는 ASCII, JPEG, MPEG 등이 있다.&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;&lt;b&gt;L5 (5 계층) - 응용 프로그램간의 연결을 지원 -&amp;gt; Message 단위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- L5 영역인 세션 계층은 세션을 열고 닫고를 제공하는 메커니즘 계층, 체크 포인트를 통해 세션 복구 지원.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통신 장치 간의 상호작용 및 동기화를 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;L4 (4 계층) - 서비스를 구분 및 데이터 전송 방식을 담당 (TCP/UDP) -&amp;gt; Segment 단위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- L4 영역인 전송 계층은 서로 다른 두 네트워크간의 전송을 담당하는 계층, &lt;u&gt;&lt;b&gt;세그멘테이션, 흐름제어, 오류 제어를 지원&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보낼 데이터의 용량과 속도, 목적지 등을 처리함. 세그멘테이션은 상위 영역의 데이터를 세그먼트라는 단위로 나누는 것을 의미, 흐름 제어의 경우 서로 처리량이 다른 네트워크 간의 전송량을 맞추는 제어 방식(stop &amp;amp; wait, sliding window), 오류제어는 전송한 데이터에 오류가 없는지 확인. 예시 프로토콜로는 &lt;u&gt;&lt;b&gt;TCP와 UDP&lt;/b&gt;&lt;/u&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;- 장비: L4 스위치&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;&lt;b&gt;L3 (3 계층) - 네트워크를 논리적으로 구분 및 연결 -&amp;gt; Packet 단위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- L3 영역인 네트워크 계층은 IP나 라우터 장비가 속한 계층, 호스트에 IP 번호를 부여하고 해당 도착지 IP까지 가는 최적의 경로를 찾음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터를 목적지까지 가장 안전하고 빠르게 전달을 목적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 장비: 라우터, L3 스위치&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;&lt;b&gt;L2 (2 계층) - 물리적 매체에 패킷 데이터를 실어 전송 -&amp;gt; Frame 단위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- L2 영역인 데이터 링크 계층은 동일한 네트워크에서의 전송을 담당 (직접 연결된 두 노드 사이), 오류제어 기능 지원, Frame으로 상위 데이터를 나눔.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 2개의 부계층이 존재 MAC(매체 접근 제어)와 LLC(논리적 연결 제어) 계층&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 물리적 연결을 통해 인접한 두 장치간에 신뢰성 있는 정보 전달을 목적으로 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 장비: 브릿지, L2 스위치&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;&lt;b&gt;L1 (1 계층) - 데이터를 신호로 변환하여 전송 -&amp;gt; Bit 단위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- L1 물리 계층은 위 데이터를 전기신호로 바꾸어 전송하는 계층, 예시로는 리피터, 허브 같은 장비가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TCP/IP Model&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP/IP 모델에서는 OSI 7계층을 아래와 같이 간소화해서 4 계층으로 표현했다.&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;- 어플리케이션(Application) + 표현(Representation) + 세션(session) = 애플리케이션 영역&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;- 네트워크(Network) = 이더넷(Ethernet)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;- 데이터 링크 + 물리 = 링크(Link)&lt;/b&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;하지만, 시간이 흘러 인터넷 개발 이후 표준이 갱신되면서 하위 레이어가 다시 세분화되었고 5 계층을 갖는 TCP/IP Updated 모델이 탄생한다. &lt;b&gt;5 계층의 모델은 링크 영역을 다시 데이터 링크와 물리 영역으로 나누고, 이더넷 영역을 네트워크로 명칭을 변경했다는 것에 차이가 있다.&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;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%;&quot;&gt;&lt;b&gt;Layer Number&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.4651%;&quot;&gt;&lt;b&gt;Name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%;&quot;&gt;&lt;b&gt;Addressing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%;&quot;&gt;&lt;b&gt;Data Unit&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%;&quot;&gt;&lt;b&gt;Protocol Example&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%; text-align: center;&quot;&gt;&lt;b&gt;L5&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.4651%; text-align: center;&quot;&gt;Application&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;-&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Message&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;HTTP, SSH, FTP, SMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%; text-align: center;&quot;&gt;&lt;b&gt;L4&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.4651%; text-align: center;&quot;&gt;Transport&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Port Number&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Segment, Datagram&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;TCP, UDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%; text-align: center;&quot;&gt;&lt;b&gt;L3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.4651%; text-align: center;&quot;&gt;Network&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IP Address&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Packet&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%; text-align: center;&quot;&gt;&lt;b&gt;L2&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.4651%; text-align: center;&quot;&gt;Data Link&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Mac Address&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Frame&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Ethernet, Wi-Fi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%; text-align: center;&quot;&gt;&lt;b&gt;L1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.4651%; text-align: center;&quot;&gt;Physical&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Bit, Signal&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&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;</description>
      <category>Programming/Infra &amp;amp; Architecture</category>
      <category>Network</category>
      <category>OSI 7</category>
      <category>OSI 7 Layer</category>
      <category>osi 7 계층</category>
      <category>TCP/IP</category>
      <category>tcp/ip model</category>
      <category>네트워크 모델</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/151</guid>
      <comments>https://johnmarc.tistory.com/151#entry151comment</comments>
      <pubDate>Mon, 25 Apr 2022 22:26:39 +0900</pubDate>
    </item>
    <item>
      <title>[인프런/호주 현직 자바 개발자가 묻고 답하는 영어 기술 면접]  강의 정리</title>
      <link>https://johnmarc.tistory.com/150</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;JVM, JRE, JDK의 차이&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5prsL/btroT6ivMQa/WFa5unjonkbtpNk4AhPsv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5prsL/btroT6ivMQa/WFa5unjonkbtpNk4AhPsv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5prsL/btroT6ivMQa/WFa5unjonkbtpNk4AhPsv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5prsL%2FbtroT6ivMQa%2FWFa5unjonkbtpNk4AhPsv1%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;661&quot; height=&quot;511&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM : 컴파일된 바이트 코드가 실행되는 머신&lt;/li&gt;
&lt;li&gt;JRE : 자바가 구동 될 수 있는 환경 (JVM + Bytecode Verifier + Class Loader)&lt;/li&gt;
&lt;li&gt;JDK : JRE + 개발을 위한 여러 toolkit (javac, javadoc.. etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AutoBoxing과 Unbokxing&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;primitive 자료형은 stack 영역에서 관리되는 가벼운 데이터&lt;/li&gt;
&lt;li&gt;wrapper class는 heap 영역에서 관리되고, 이에 대한 주소 참조 값이 stack영역에서 관리됨.&lt;/li&gt;
&lt;li&gt;Autoboxing : primitive 자료형 (int, long, float, double)이 이에 대응되는 Wrapper Class Integer, Long, Float, Double로 형변환이 되는 것&lt;/li&gt;
&lt;li&gt;UnBoxing : Wrapper Class에서 Primtive 자료형으로 자동 형변환 되는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java 메모리 구조에서 stack 영역과 heap 영역이 무엇인지 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stack : primitive 데이터, reference 값들만 저장되고 관리됨.&lt;/li&gt;
&lt;li&gt;heap : object, 크기가 큰 데이터들을 관리하며 이들을 관리하는 GC(Garbage Collectoin)이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;String, String Buffer, String Builder&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;String By literal : String Pool이라는 Heap 메모리 공간 내에 특수한 영역에서 문자열이 생성되고, 한번 생성되면 값이 변하지 않는다. 문자열이 재 사용이 된다.&lt;/li&gt;
&lt;li&gt;String By Class : new 키워드를 통해 생성된 String Class의 인스턴스는 생성 시마다 매번 새로운 주소 값을 갖고 새로 생성되므로 문자열 값이 재 사용되지 않는다.&lt;/li&gt;
&lt;li&gt;String Buffer : 문자열 값을 가변적으로 바꿀 수 있다. Synchronized 키워드를 통해 Thread-Safe 하게 값을 변경할 수 있다.&lt;/li&gt;
&lt;li&gt;String Builder : 문자열 값을 가변적으로 바꿀 수 있다. Thread-Safe 하지 않다. Synchronized 하지 않아 Sring Buffer 보단 속도가 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Overloading과 Overriding&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;overloading : 한 클래스 내에서 메소드 이름이 같고 메소드의 매게 변수가 다른 메소드들, 리턴 타입이 같거나 달라도 된다. (ex: System.println)&lt;/li&gt;
&lt;li&gt;overriding : 자식 클래스가 부모 클래스의 메소드를 재 정의한 것, 매개변수는 같고, 리턴 타입은 같거나 리턴 클래스의 하위 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리터럴과 new를 통해 생성된 String의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리터럴을 통해 생성된 String은 String Pool에 저장되고 관리됨. String Pool은 Heap 메모리 내에 존재하는 특정한 구역. String Pool이기에 동일한 String의 경우는 생성하지 않고 이전의 값을 재사용한다. (주소 값이 같다.)&lt;/li&gt;
&lt;li&gt;new 키워드를 통해 생성된 String은 heap 메모리에 매번 새로 생성되어 관리됨. 동일한 String을 사용한다고 해도 new 키워드를 통해 생성했다면 서로 다른 객체이기에 주소 값이 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Static 이란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java는 프로그램을 구동하기 위해 객체를 생성해서 메모리에 올려야 한다. 하지만 static 은 프로그램에 의해 객체가 생성되는 것이 아닌 클래스가 로딩될 때 생성된다.&lt;/li&gt;
&lt;li&gt;클래스의 이름으로 접근이 가능하며, 해당 클래스의 객체와는 무관함 (객체가 생성되기 전에 이미 메모리에 올라가므로)&lt;/li&gt;
&lt;li&gt;자주 사용되는 변수, 자주 호출되는 메소드 (ex: Math.rand())&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Abstract와 Interface의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;super 클래스가 어느 정도 구현되어있으며, 구현의 완성을 sub class에 맡기는 경우를 abstract class이며 abstract method가 있다. 모든 접근 제어자 사용 가능&lt;/li&gt;
&lt;li&gt;실질적으로 전혀 구현이 없는 class, 구현에 대한 명세만 작성되어있고 이를 구현하는 sub class에서 구현해야 함. 오직 public 접근 제어자만 사용, 다중 상속 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java에서의 Exception Handling&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Exception이란 프로그램에서 처리 가능한 수준의 문제 발생 말한다.&lt;/li&gt;
&lt;li&gt;Error는 프로그램에서 처리 불가능한 심각한 수준의 문제 발생을 의미한다(EX: OOM).&lt;/li&gt;
&lt;li&gt;Exception의 경우, Compile time에서 발생할 수 있는 Checked Exception과 실행 시 발생할 수 있는 Unchecked Exception이 있다.&lt;/li&gt;
&lt;li&gt;Exception Handling이란, 이러한 Exception이 발생했을 때 프로그램에서 어떻게 잘 처리할 수 있는 지를 의미하며 방법은 다음과 같이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;try-catch block : try-catch block 문을 통해 exception이 발생한 부분에서 exception에 따라 프로그램을 핸들링.&lt;/li&gt;
&lt;li&gt;throws : exception이 발생한 메소드에서 exception handling을 자신을 호출한 상위 method로 처리를 넘겨 처리를 회피하는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Errors, Unchecked Exception, Checked Exception들의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exception
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;checked exception : 프로그램 실행 전에 확인이 가능하거나 예측이 가능한 exception, compile time exception이라고도 함. (ex: IOException, SQLException)&lt;/li&gt;
&lt;li&gt;unchecked exception : 런타임 때 발생하는 예측 불가능한 exception (eg: ArrayIndexOutOfBoundException, NPE)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;error : exception handling이 어려운 심각한 수준의 exception을 의미하며 (EX: OOM)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;throw와 throws의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;throw : exception을 발생하는 키워드, 객체를 생성해서 예외를 발생, 메소드 내부에서 사용되고 오직 한 가지의 exception만 발생시킬 수 있다.&lt;/li&gt;
&lt;li&gt;throws : exception 처리를 상위 메소드로 넘기는 키워드, caller에 exception handling을 넘김, 여러 인터페이스를 상속할 수 있다. 메소드 선언부에서만 사용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Byte Stream과 Character Stream의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stream은 소스 데이터와 프로그램 간의 데이터 교환 관련 통로&lt;/li&gt;
&lt;li&gt;byte stream - reading/writing of any bytes, raw data&lt;/li&gt;
&lt;li&gt;InputStream/OuputStream&lt;/li&gt;
&lt;li&gt;character stream - reading/writing of characters , text , reader / writer class , 2byte for unicode&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;try-catch-finally block에서의 system.exit() 메소드의 호출에 따른 차이는?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;System.exit() 소프트웨어적으로 강제적으로 프로세스를 종료하는 함수&lt;/li&gt;
&lt;li&gt;finally는 error 조건에 따라 영향을 받진 않는다 &amp;rarr; 프로세스가 살아있는 한 반드시 실행된다.&lt;/li&gt;
&lt;li&gt;System.exit() 메소드 호출 후 finally가 호출된다면 &amp;rarr; 프로세스가 죽었기에 finally block은 실행되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ArrayList와 LinkedList의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List는 원소들의 순서를 보장하는 인터페이스&lt;/li&gt;
&lt;li&gt;Vector와 ArrayList의 차이?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vector는 원소가 다 찬 경우 2배로 사이즈를 크기를 키움. 스레드 동기화 지원&lt;/li&gt;
&lt;li&gt;ArrayList는 원소가 다 찬 경우 50%로 사이즈를 키움, 스레드 동기화를 지원하지 않음 &amp;rarr; vector 보다 속도가 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ArrayList와 LinkedList 차이?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ArrayList : 차례대로 원소가 배열되어있음, 조회에는 속도가 좋고(인덱스로 접근 가능) 삭제 or 추가에 속도가 느리다 (재배치가 이루어져야 하므로).&lt;/li&gt;
&lt;li&gt;LinkedList : 삽입이나 삭제는 속도가 빠름, 조회는 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Enumeration과 Iterator의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;enumeration은 원본의 복제본을 만들고 순차적으로 접근함 (멀티 스레드 환경에서 데이터 불일치가 발생할 수 있음), 초기 몇몇 데이터 구조만 지원 (Vector, HashTable)&lt;/li&gt;
&lt;li&gt;interator는 직접 원본 데이터에 접근하므로 데이터 불일치는 발생할 수 없음 (예외 발생시킴), 원본 데이터를 직접 다루기 때문에 remove() 메소드를 지원함. 모든 데이터 구조를 지원함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;maker interface&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;컴파일러나 JVM에게 특정한 행동을 하도록 알리기 위한 인터페이스&lt;/li&gt;
&lt;li&gt;Serializable - 해당 인터페이스를 구현한 클래스의 객체는, 컴파일러는 나중에 Stream에 의해 객체 직렬화될 수 있도록 준비함.&lt;/li&gt;
&lt;li&gt;어노테이션으로 대체할 수 있음 (junit @Test)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread의 생성 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Thread는 start() 메소드를 호출해야 실행됨.&lt;/li&gt;
&lt;li&gt;멀티 스레드 지원&lt;/li&gt;
&lt;li&gt;리소스를 공유할 수 있음 (heap) &amp;rarr; dead lock 발생할 수 있음&lt;/li&gt;
&lt;li&gt;Thread Class를 상속받아 새로운 Thread 를 만들고, run() 메소드를 오버 라이딩&lt;/li&gt;
&lt;li&gt;Runnable 인터페이스를 구현, Runnable의 run() 메소드를 구현한 뒤, 별도의 스레드의 생성자로 넘겨 start() 메소드를 통해 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Deadlock이란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티스레드 환경에서 발생, 서로의 리소스를 요청하며 대기 중인 상태&lt;/li&gt;
&lt;li&gt;방지법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1개의 상의 lock을 방지&lt;/li&gt;
&lt;li&gt;중첩된 lock 사용 지향&lt;/li&gt;
&lt;li&gt;lock의 순서를 동일하게 구성&lt;/li&gt;
&lt;li&gt;멀티스레드가 필요 없다면 싱글 스레드 지양&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;main메소드의 static 키워드를 지운다면?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main() 메소드는 프로그램의 진입점&lt;/li&gt;
&lt;li&gt;public static void main(String[] args)로 정형화됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;public - access from anywhere&lt;/li&gt;
&lt;li&gt;static - loaded first in memory&lt;/li&gt;
&lt;li&gt;void - return type&lt;/li&gt;
&lt;li&gt;main - method name&lt;/li&gt;
&lt;li&gt;String[] args - command line parameters in runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;static 키워드가 없다면 jvm 이 프로그램을 실행하지 못함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키워드의 순서가 바뀌는 경우? &amp;rarr; 상관없이 실행됨&lt;/li&gt;
&lt;li&gt;main() 메소드에 final이 붙은 경우? &amp;rarr; 상관없음. main() 메소드를 상속받아 구현하는 경우는 없기에.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PrepareStatement가 Statement 보다 선호되는 이유는?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;preparestatement는 statement의 sub inteface, run time 시 파라미터를 sql 쿼리에 넘기는 방식&lt;/li&gt;
&lt;li&gt;특정 쿼리를 자주 호출하는 경우 &amp;rarr; 이미 쿼리가 작성되어있고 파라미터만 넘기니 효율이 좋음&lt;/li&gt;
&lt;li&gt;sql 인젝션에도 안전 (이미 작성된 sql에 파라미터만 넘겨서 사용하므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JDBC&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;database에 쿼리를 실행할 수 있는 java 표준 api&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;disc 방법 : 디스크에 이미지 파일을 저장하고, 파일 경로를 db에 저장
&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;li&gt;database 방법 : 실제 데이터베이스에 이미지를 저장하는 방법. 이미지 사이즈가 작은 경우 적합. blob 타입으로 저장&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 data-ke-size=&quot;size23&quot;&gt;[레퍼런스]&lt;/h3&gt;
&lt;figure id=&quot;og_1640499329879&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;호주 현직 자바 개발자가 묻고 답하는 영어 기술면접 25 - 인프런 | 강의&quot; data-og-description=&quot;는 자바 언어 프로그래머로서 일하고자 하는 모든 분들을 위한 것입니다. 자바 언어는 전세계적으로 그 수요가 꾸준히 지속되는 컴퓨터 언어인 만큼 해외취업을 원하는 분들뿐만 아니라 국내취&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%98%81%EC%96%B4-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%98%81%EC%96%B4-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c9YbJu/hyMRGS1SoM/IQlxkbKP136gxH4ZkAeSZ0/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512,https://scrap.kakaocdn.net/dn/cpbLgo/hyMRGrW86E/K6C5ipckk18ZAAPvOv6cF0/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%98%81%EC%96%B4-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%98%81%EC%96%B4-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c9YbJu/hyMRGS1SoM/IQlxkbKP136gxH4ZkAeSZ0/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512,https://scrap.kakaocdn.net/dn/cpbLgo/hyMRGrW86E/K6C5ipckk18ZAAPvOv6cF0/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512');&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;호주 현직 자바 개발자가 묻고 답하는 영어 기술면접 25 - 인프런 | 강의&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;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Java</category>
      <category>inflearn</category>
      <category>Java 기술 면접</category>
      <category>java 면접질문</category>
      <category>Java 정리</category>
      <category>인프런</category>
      <category>해외 Java 개발자</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/150</guid>
      <comments>https://johnmarc.tistory.com/150#entry150comment</comments>
      <pubDate>Sun, 26 Dec 2021 15:15:34 +0900</pubDate>
    </item>
    <item>
      <title>[DB,Transaction] Transaction 과 Lock</title>
      <link>https://johnmarc.tistory.com/149</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;트랜잭션이란.&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5tdzk/btroZFRJzZU/ILssB7Ce6C3PH01hzKVSiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5tdzk/btroZFRJzZU/ILssB7Ce6C3PH01hzKVSiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5tdzk/btroZFRJzZU/ILssB7Ce6C3PH01hzKVSiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5tdzk%2FbtroZFRJzZU%2FILssB7Ce6C3PH01hzKVSiK%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;881&quot; height=&quot;446&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스의 데이터를 조작하는 작업의 단위.&lt;/li&gt;
&lt;li&gt;트랜잭션은 이론적으로 ACID 원칙을 보장해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Atomicity (원자성) : 트랜잭션의 부분 작업만 성공할 순 없다. 전체가 성공하거나, 전체가 실패해야 한다.&lt;/li&gt;
&lt;li&gt;Consistency (일관성) : 트랜잭션이 성공하면 언제나 일관성이 있는 데이터베이스 상태를 유지하는 것을 의미. 만약 모든 계좌는 잔고가 있어야 한다면, 이를 위반하는 트랜잭션은 중단된다.&lt;/li&gt;
&lt;li&gt;Isolation (독립성) : 하나의 트랜잭션이 실행되고 있을 때, 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장하는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;Durability (지속성) : 성공적으로 수행된 트랜잭션은 영원이 반영되어야 한다. 모든 트랜잭션은 로그로 남고, 시스템 장애 발생 전으로 되돌릴 수 있다. 트랜잭션은 로그에 모든 것이 저장된 후에만 commit 상태로 간주될 수 있다.&lt;/li&gt;
&lt;li&gt;하지만 실제로는 ACID 원칙이 모두 완벽하게 지켜지지는 않는다.
&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;따라서 각 level 별 transaction lock level이 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Inno DB에서의 Lock&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InnoDB는 transaction의 ACID 원칙과 동시성을 최대한 보장하기 위해 다양한 종류의 lock을 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Row Lock&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블이 row에 락을 거는 것으로 2가지 유형이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shared Lock : select (read)에 대한 lock이다. 동일한 읽기를 보장하며, read 작업을 할 때 innoDB가 각 row에 락을 건다.&lt;/li&gt;
&lt;li&gt;Exclusive Lock : write에 대한 lock이다. update, delete 구문 등 데이터 수정 쿼리 날릴 때 row에 걸리는 lock이다.&lt;/li&gt;
&lt;li&gt;Shared Lock과 Exclusive Lock을 거는 조건
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시에 여러 transaction이 한 row에 shared lock을 걸 수 있다. 즉 동시에 여러 transaction이 row를 읽을 수 있다.&lt;/li&gt;
&lt;li&gt;shared lock 이 걸려있는 row에 다른 transaction이 exclusive lock을 걸 순 없다. 즉 다른 transaction이 row를 읽는 동안 수정/삭제가 불가능하다.&lt;/li&gt;
&lt;li&gt;exclusive lock이 걸려있는 동안 다른 transaction들은 shared lock과 exclusive lock 모두 걸 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Record Lock&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Table의 row에 걸리는 lock이 아닌, DB의 Index Record에 걸리는 lock이다. Row Lock과 마찬가지로 Shared Lock과 Exclusive Lock이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Gap Lock&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Index Record의 Gap 사이에 걸리는 lock.&lt;/li&gt;
&lt;li&gt;record 테이블에서 인덱스와 인덱스 사이 즉 record가 없는 부분에 걸리는 lock이다.&lt;/li&gt;
&lt;li&gt;record lock은 이미 있는 row가 변경되지 않도록 하기 위해 사용되는 반면, gap lock은 새로운 row가 추가되는 것을 막기 위해 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lock이 걸리고 풀리는 타이밍&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lock은 트랜잭션이 Begin으로 시작될 때 걸리고, commit 또는 rollback 될 때 풀린다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming/Database</category>
      <category>Database</category>
      <category>Inno DB</category>
      <category>MySQL</category>
      <category>transaction</category>
      <category>개념</category>
      <category>데이터베이스</category>
      <category>정리</category>
      <category>트랜잭션</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/149</guid>
      <comments>https://johnmarc.tistory.com/149#entry149comment</comments>
      <pubDate>Sun, 26 Dec 2021 14:36:23 +0900</pubDate>
    </item>
    <item>
      <title>[System Design] 1. 사용자 수에 따른 규모의 확장성</title>
      <link>https://johnmarc.tistory.com/148</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;* 확장의 종류&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 수직적 확장 (Scale-Up)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- RAM, CPU 등의 주요 리소스를 추가적으로 더 할당하여, 성능을 확보하는 확장의 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스의 규모가 작고, 트래픽양이 적은 경우 적합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[장점]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버 관리가 상대적으로 용이함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가장 쉽고 간단한 확장 방법&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;- 장애에 대한 자동복구(failorver), 다중화(re-dundancy) 전략을 제시하지 못함, 서버에 장애가 발생하면&lt;br /&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;2) 수평적 확장 (Scalue-Out)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동일한 역할을 하는 서버를 추가로 배치하여, 요청을 분산 처리하여 성능을 확보하는 확장의 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스의 규모가 크고, 트래픽양이 많은 경우 적합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[장점]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 확장의 한계가 없음 (여유가 되는 대로 서버를 계속 증설하면 됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 장애에 대한 자동복구(failover), 다중화(re-dundancy) 전략이 다양함, 특정 서버에 장애가 발생해도 전체 장애로&lt;br /&gt;&amp;nbsp; 이어지지 않으며 서비스를 지속적으로 제공할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[단점]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버 관리가 상대적으로 여려움 (많은 서버를 동시에 관리해야 하므로)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 단일 서버&lt;/b&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;/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;- 특정 영역에 대한 규모의 확장이 어려움 (ex: 데이터베이스 성능을 높이기 위해, 메모리 또는 CPU를 올리고 싶은 경우)&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;b&gt;2) 서비스 서버와 데이터베이스 서버의 분리&lt;/b&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;- 기존의 단일 서버 구성과 달리 데이터베이스 서버를 하나 더 운영하므로, 운영비용이 더 들게 된다.&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;&lt;b&gt;* 어떤 데이터베이스를 사용해야 하는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RDBMS&lt;/b&gt; - &lt;b&gt;&lt;u&gt;데이터의 정합성이 중요&lt;/u&gt;&lt;/b&gt;하고, 다루는 &lt;b&gt;&lt;u&gt;데이터가 정형적(structured)&lt;/u&gt;&lt;/b&gt;일 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NoSQL&lt;/b&gt; - &lt;u&gt;&lt;b&gt;낮은 응답 지연시간(latency)이&lt;/b&gt;&lt;/u&gt; 요구되는 경우, 다루는 &lt;u&gt;&lt;b&gt;데이터가 비정형적(unstructured)&lt;/b&gt;&lt;/u&gt;일 때, 데이터를 직렬화(Serialize)하거나 역직렬화(deserialize)하기만 하면 될 때, &lt;u&gt;&lt;b&gt;아주 많은 데이터를 저장할 필요&lt;/b&gt;&lt;/u&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) 로드밸런서&lt;/b&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;&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;- 수평적 확장을 사용하는 경우 로드 밸런서를 통해, 자동복구(failover)를 지원하게 되며 웹 계층의 &lt;br /&gt;&amp;nbsp; 가용성 (availability)는 향상됨&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;br /&gt;&amp;nbsp; &amp;nbsp;트래픽을 분산해서 웹 서버 계층 서버에 전달함&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4) 데이터베이스 다중화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 통상 많은 데이터베이스 관리 시스템 (DBMS)가 다중화를 지원한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보통 서버 사이에&lt;b&gt; 주(Master) - 부(Slave)&lt;/b&gt; &lt;b&gt;관계를 설정&lt;/b&gt;하고 &lt;u&gt;&lt;b&gt;데이터 원본은 주 서버&lt;/b&gt;&lt;/u&gt;에, &lt;u&gt;&lt;b&gt;사본은 부 서버&lt;/b&gt;&lt;/u&gt;에 저장하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 쓰기 (Write) 연산은&amp;nbsp;&lt;b&gt;주(Master)&lt;/b&gt; 서버에서만 지원하고, &lt;b&gt;부(Slave)&amp;nbsp;&lt;/b&gt;서버는 읽기만 지원한다&lt;br /&gt;&amp;nbsp; (ex: insert, update, delete -&amp;gt; 주 서버, select -&amp;gt; 부 서버)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 대부분의 애플리케이션은 쓰기 연산보다 읽기 연산의 비중이 높음, 따라서 통상적으로&amp;nbsp;&lt;b&gt;부(Slave) 서버의&lt;/b&gt; 수가 더 많다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[장점]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주-부 다중화 모델에서 각 연산(쓰기(insert, update, delete), 읽기(select))이 분산되므로 더 나은 성능을 보장한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자연재해 등 여러 이유로 데이터베이스 서버 가운데 일부가 파괴되어도 데이터는 보존이 됨, 지역적으로 떨어진 &lt;br /&gt;&amp;nbsp; 장소에 다중화가 가능하기 때문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터를 여러 지역에 복제해 둠으로, 하나의 데이터베이스에서 장애가 발생해도 다른 데이터베이스 서버에서 &lt;br /&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;* 부 서버(Slave)가 다운된 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 부 서버가 한 대 라면, 일시적으로 부 서버로 가던 읽기 연산이 주 서버로 가게 되고, 즉시 새로운 부 데이터베이스 서버가 장애 서버를 대체할 것임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 부 서버가 여러 대인 경우 읽기 연산은 나머지 부 데이터베이스 서버로 분산되어 처리될 것이며, 새로운 부 데이터베이스가 장애 서버를 대체할 것임&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;* 주 서버(Master)가 다운된 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한 대의 부 데이터베이스만 있는 경우 해당 부 데이터베이스가 주 데이터베이스로 승격되어 &lt;br /&gt;&amp;nbsp; 일시적으로 읽기, 쓰기 연산을 모두 처리하며, 새로운 주 데이터베이스 서버가 장애 서버를 대체하면 쓰기 연산은 &lt;br /&gt;&amp;nbsp; 새로운 주 데이터베이스에서만 이루어진다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 여러 대의 부 데이터베이스가 있는 경우, 특정 한 대의 부 데이터베이스가 주 데이터베이스로 승격되고 쓰기 연산은 승격된 주 데이터베이스에서 이루어지다가, 새로운 주 데이터베이스가 장애 서버를 대체하면 새로운 주 데이터베이스에서만 쓰기 연산을 수행하고 이전에 승격된 주 데이터베이스 서버는 다시 부 데이터베이스로 변경이 되어 읽기 연산만 수행한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* 장애 상황에서 부 서버에 보관된 데이터가 최신이 아닌 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터 복구 스크립트를 돌려서 누락된 데이터를 추가한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다중 마스터 또는 원형 다중화 방식을 도입하여 데이터의 최신성을 보장한다&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;h3 data-ke-size=&quot;size23&quot;&gt;출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/102819435&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://www.yes24.com/Product/Goods/102819435&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1634218984155&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;가상 면접 사례로 배우는 대규모 시스템 설계 기초 - YES24&quot; data-og-description=&quot;&amp;ldquo;페이스북의 뉴스 피드나 메신저, 유튜브, 구글 드라이브 같은 대규모 시스템은 어떻게 설계할까?&amp;rdquo;IT 경력자라도 느닷없이 대규모 시스템을 설계하려고 하면 막막하다고 느낄 수 있다. 특히나&quot; data-og-host=&quot;www.yes24.com&quot; data-og-source-url=&quot;http://www.yes24.com/Product/Goods/102819435&quot; data-og-url=&quot;http://www.yes24.com/Product/Goods/102819435&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/f1MeA/hyLYyuWkHh/wHfYuHOvu917PPCGZcicu1/img.jpg?width=912&amp;amp;height=1200&amp;amp;face=0_0_912_1200,https://scrap.kakaocdn.net/dn/cnNSGm/hyLXCsdoRv/69JBMkn799cPUp2wnI99P1/img.jpg?width=912&amp;amp;height=1200&amp;amp;face=0_0_912_1200&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/102819435&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.yes24.com/Product/Goods/102819435&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/f1MeA/hyLYyuWkHh/wHfYuHOvu917PPCGZcicu1/img.jpg?width=912&amp;amp;height=1200&amp;amp;face=0_0_912_1200,https://scrap.kakaocdn.net/dn/cnNSGm/hyLXCsdoRv/69JBMkn799cPUp2wnI99P1/img.jpg?width=912&amp;amp;height=1200&amp;amp;face=0_0_912_1200');&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;가상 면접 사례로 배우는 대규모 시스템 설계 기초 - YES24&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;페이스북의 뉴스 피드나 메신저, 유튜브, 구글 드라이브 같은 대규모 시스템은 어떻게 설계할까?&amp;rdquo;IT 경력자라도 느닷없이 대규모 시스템을 설계하려고 하면 막막하다고 느낄 수 있다. 특히나&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.yes24.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>Programming/Infra &amp;amp; Architecture</category>
      <category>System Design</category>
      <category>데이터베이스 다중화</category>
      <category>로드 밸런서</category>
      <category>스케일 아웃</category>
      <category>스케일 업</category>
      <category>시스템 디자인</category>
      <category>인프라</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/148</guid>
      <comments>https://johnmarc.tistory.com/148#entry148comment</comments>
      <pubDate>Thu, 14 Oct 2021 22:43:26 +0900</pubDate>
    </item>
    <item>
      <title>[Programmer Weekly Challenge #1] 부족한 금액 계산하기</title>
      <link>https://johnmarc.tistory.com/147</link>
      <description>&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;이 놀이기구의 원래 이용료는 price원인데,&lt;br /&gt;놀이기구를 N 번 째 이용한다면 원래 이용료의 N배를 받기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 처음 이용료가 100이었다면 2번째에는 200, 3번째에는 300으로 요금이 인상됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀이기구를 count번 타게 되면 현재 자신이 가지고 있는 금액에서 얼마가 모자라는지를 return 하도록 solution 함수를 완성하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 금액이 부족하지 않으면 0을 return 하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한사항&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;놀이기구의 이용료 price : 1 &amp;le; price &amp;le; 2,500, price는 자연수&lt;/li&gt;
&lt;li&gt;처음 가지고 있던 금액 money : 1 &amp;le; money &amp;le; 1,000,000,000, money는 자연수&lt;/li&gt;
&lt;li&gt;놀이기구의 이용 횟수 count : 1 &amp;le; count &amp;le; 2,500, count는 자연수&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;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 38px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;Price&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;Money&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;Count&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;Result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;20&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 설명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 #1&lt;br /&gt;이용금액이 3인 놀이기구를 4번 타고 싶은 고객이 현재 가진 금액이 20이라면, 총 필요한 놀이기구의 이용 금액은 30&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;(= 3+6+9+12)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 되어 10만큼 부족하므로 10을 return 합니다.&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;p data-ke-size=&quot;size16&quot;&gt;풀이 방법)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 반복문을 돌려서 계산을 하는 방법이 가장 먼저 생각났으나, 좀 더 효율적인 방법이 없을까 하고 문제에서 규칙을 찾게 되었다.&lt;br /&gt;&lt;br /&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;br /&gt;(이용금액(3) * 이용 횟수(1) + 이용금액(3) * 이용 횟수(2) + 이용금액 (3) * 이용 횟수(3) + 이용금액(3) * 이용 횟수(4) ) &lt;br /&gt;= 전체 이용 금액&lt;br /&gt;&lt;br /&gt;이용금액(3) * (1 + 2 + 3 + 4) = 전체 이용금액&lt;br /&gt;&lt;br /&gt;따라서 1부터 이용 횟수까지의 합을 이용금액으로 곱한 값이 전체 이용금액이 되며 여기서 가우스 정리를 이용하면&amp;nbsp;&lt;br /&gt;이용금액 * ((1 + 이용 횟수) * 이용횟수) / 2)와 같이 식을 정리할 수 있고, price * ((1 + count) * count) / 2); 가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 코드는 아래와 같이 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1628482358376&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public long solution(int price, int money, int count) {
        long answer = (long)price * (long)(((1+count) * count) / 2);
        if(answer - money &amp;gt; 0){
            return answer - (long)money;
        }
        return 0;
    }
}&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;위 코드를 Math.max() 메서드를 활용하면 아래와 같이 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1628482550544&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public long solution(int price, int money, int count) {
        return Math.max(price * (count * (count + 1) / 2) - money, 0);
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming/Algorithm</category>
      <category>Algorithm</category>
      <category>Java</category>
      <category>가우스 정리</category>
      <category>알고리즘</category>
      <category>자바 알고리즘</category>
      <category>프로그래머스</category>
      <category>프로그래머스 위클리 챌린지</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/147</guid>
      <comments>https://johnmarc.tistory.com/147#entry147comment</comments>
      <pubDate>Mon, 9 Aug 2021 13:16:58 +0900</pubDate>
    </item>
    <item>
      <title>[백 투 더 베이직] 객체지향 SOLID 원칙</title>
      <link>https://johnmarc.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;SOLID원칙&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SRP(Single Responsibility Principle): 단일 책임의 원칙 &lt;br /&gt;&amp;rarr; 클래스는 단 하나의 책임을 가져야 하며, 클래스를 변경하는 이유는 단 하나의 이유여야 한다.&lt;/li&gt;
&lt;li&gt;OCP(Open-Closed Principle): 개방 폐쇄 원칙 &lt;br /&gt;&amp;rarr; 확장에는 열려있어야 하며, 변경에는 닫혀있어야 한다.&lt;/li&gt;
&lt;li&gt;LSP(Liskove Substitution Principle): 리스코프 치환 원칙 &lt;br /&gt;&amp;rarr; 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작 해야한다.&lt;/li&gt;
&lt;li&gt;ISP(Interface Segregation Principle): 인터페이스 분리 원칙 &lt;br /&gt;&amp;rarr; 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.&lt;/li&gt;
&lt;li&gt;DIP(Dependency Inversion Principle): 의존 역전 원칙 &lt;br /&gt;&amp;rarr; 고수준 모듈은 저수준 모듈의 구현에 대해 의존하면 안 된다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Programming/Java</category>
      <category>Java</category>
      <category>OOP</category>
      <category>OOP SOLID Principle</category>
      <category>SOLD 원칙</category>
      <category>객체지향</category>
      <category>객체지향 기초</category>
      <category>자바</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/146</guid>
      <comments>https://johnmarc.tistory.com/146#entry146comment</comments>
      <pubDate>Mon, 19 Jul 2021 22:17:25 +0900</pubDate>
    </item>
    <item>
      <title>[삽질일기] Arrays.asList 와 ArrayList의 차이</title>
      <link>https://johnmarc.tistory.com/145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;코딩을 하다 보면, 배열 또는 List 형태가 아닌 데이터를 List 데이터 타입으로 만들 때 간편하게 Arrays.asLIst 메서드를 활용해서 그동안 리스트 데이터를 만들고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1626509245165&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; score1 = Arrays.asList(1, 2, 3, 4, 5);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러다가 Arrays.asList로 만든 List A에서 DB에서 조회한 List B의 값을 빼야 하는 경우가 생겼고 아무 생각 없이 다음과 &lt;br /&gt;같이 코드를 작성해봤다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1626509933769&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; score1 = Arrays.asList(1, 2, 3, 4, 5);
// DB에서 값을 가져옴
List&amp;lt;Integer&amp;gt; score2 = getSomeDataFromDatabase();
// DB 값을 제외한 나머지 값을 찾아보자!
score1.removeAll(score2);&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;그랬더니, UnsupportedOperationException 예외가 발생했다. 한마디로 해당 연산은 지원하지 않는다는 에러이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3589&quot; data-origin-height=&quot;776&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W7QF9/btq9IZ0pwHl/JP3U6qsHbjtKyBP2LNEg01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W7QF9/btq9IZ0pwHl/JP3U6qsHbjtKyBP2LNEg01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W7QF9/btq9IZ0pwHl/JP3U6qsHbjtKyBP2LNEg01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW7QF9%2Fbtq9IZ0pwHl%2FJP3U6qsHbjtKyBP2LNEg01%2Fimg.png&quot; data-origin-width=&quot;3589&quot; data-origin-height=&quot;776&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;removeAll과 같은 메서드는 List 인터페이스를 구현한 구현체에 모두 적용되는데, 같은 ArrayList인데 왜 해당 에러가 발생할까?라는 생각이 들어 Arrays.asList의 메서드 코드를 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1845&quot; data-origin-height=&quot;1477&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvK0dM/btq9NQn1oVk/bbk6rJGPSWKohDUfreXEJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvK0dM/btq9NQn1oVk/bbk6rJGPSWKohDUfreXEJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvK0dM/btq9NQn1oVk/bbk6rJGPSWKohDUfreXEJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvK0dM%2Fbtq9NQn1oVk%2Fbbk6rJGPSWKohDUfreXEJK%2Fimg.png&quot; data-origin-width=&quot;1845&quot; data-origin-height=&quot;1477&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 보고 약간 충격을 받았다. 리턴 타입 클래스명이 ArrayList이고, List 타입 변수에 할당이 잘 되기에 평소에 하는 java.util 패키지에 있는 ArrayList 클래스를 리턴할 줄 알았는데, Arrays 클래스의 inner class인 ArrayList를 리턴하는 것이었다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrays.asLIst의 메서드 설명 부분을 보면, java.util 패키지에 있는 ArrayList와 확연한 차이를 알 수 있는데 바로 Arrays.asLIst로 리턴되는 java.util.Arrays.ArrayList는 고정크기의 배열을 가진 리스트를 리턴한다는 것이었다. 따라서 크기가 변경되는 동작인 remove, removeAll, add 등의 연산이 지원되지 않는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 확인해보기 위해 다음과 같이 테스트 코드를 작성해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1626512612677&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.tistory.johnmark;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class ArraysOperationTest {
	@Test
	@DisplayName(&quot;Arrays removeAll 테스트&quot;)
	void arraysRemoveAll() {
		// given
		List&amp;lt;Integer&amp;gt; score1 = Arrays.asList(1, 2, 3, 4, 5);
		List&amp;lt;Integer&amp;gt; score2 = new ArrayList&amp;lt;&amp;gt;();
		score2.add(1);
		score2.add(2);

		try {
			// when
			score1.removeAll(score2);
		} catch (Exception e) {
			System.out.println(&quot;----------------------------------&quot;);
			System.out.println(&quot;ArraysOperationTest.arraysRemoveAll&quot;);
			System.out.println(&quot;Exception = &quot; + e);
			// then
			Assertions.assertThat(e).isInstanceOf(UnsupportedOperationException.class);
		}
	}

	@Test
	@DisplayName(&quot;Arrays add 테스트&quot;)
	void arraysAddTest() {
		// given
		List&amp;lt;Integer&amp;gt; score = Arrays.asList(1, 2, 3, 4, 5);

		try {
			// when
			score.add(6);
		} catch (Exception e) {
			System.out.println(&quot;----------------------------------&quot;);
			System.out.println(&quot;ArraysOperationTest.arraysAddTest&quot;);
			System.out.println(&quot;Exception = &quot; + e);
			// then
			Assertions.assertThat(e).isInstanceOf(UnsupportedOperationException.class);
		}
	}

	@Test
	@DisplayName(&quot;Arrays remove 테스트&quot;)
	void arraysRemoveTest() {
		// given
		List&amp;lt;Integer&amp;gt; score = Arrays.asList(1, 2, 3, 4, 5);

		try {
			// when
			score.remove(5);
		} catch (Exception e) {
			System.out.println(&quot;----------------------------------&quot;);
			System.out.println(&quot;ArraysOperationTest.arraysRemoveTest&quot;);
			System.out.println(&quot;Exception = &quot; + e);
			// then
			Assertions.assertThat(e).isInstanceOf(UnsupportedOperationException.class);
		}
	}
}&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;데이터 크기가 변경하는 연산을 수행했을 때, 반드시 Exception이 발생되고, 해당 Exception은 UnsupportedOperationExcception이여야 테스트를 통과하는 코드이다.&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;테스트를 수행한 결과는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3530&quot; data-origin-height=&quot;761&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yLmYI/btq9Rd3C3oq/VKkHxOonhBk6sG4VUjfT81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yLmYI/btq9Rd3C3oq/VKkHxOonhBk6sG4VUjfT81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yLmYI/btq9Rd3C3oq/VKkHxOonhBk6sG4VUjfT81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyLmYI%2Fbtq9Rd3C3oq%2FVKkHxOonhBk6sG4VUjfT81%2Fimg.png&quot; data-origin-width=&quot;3530&quot; data-origin-height=&quot;761&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 계기를 통해, 내가 사용하는 코드가 정확한 어떤 코드인지 어떤 데이터를 리턴하고 해당 데이터의 타입은 무엇인지 확인을 잘해야 한다는 생각이 들었고, 기본기에 대한 중요성을 다시 한번 깨닫게 되었다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ UnsupportedOperationException을 회피하는 방법&lt;/p&gt;
&lt;pre id=&quot;code_1626512897077&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; score = new ArrayList&amp;lt;&amp;gt;(Arrays.asList(1,2,3));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 위와 같이 ArrayList로 wrapping 하면 된다.&lt;/p&gt;</description>
      <category>Programming/Java</category>
      <category>ArrayList vs Arrays.asList</category>
      <category>Arrays</category>
      <category>Java</category>
      <category>java ArrayList</category>
      <category>java util</category>
      <category>list</category>
      <category>UnsupportedOperationException</category>
      <category>기본기</category>
      <category>삽질일기</category>
      <category>자바</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/145</guid>
      <comments>https://johnmarc.tistory.com/145#entry145comment</comments>
      <pubDate>Sat, 17 Jul 2021 18:11:17 +0900</pubDate>
    </item>
    <item>
      <title>[Java8] 함수형 인터페이스와 람다 표현식</title>
      <link>https://johnmarc.tistory.com/144</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;java 8 버전을 꽤 오래 사용했었는데, 제대로 공부한 적이 없는 것 같아 조금씩 정리해보는 포스팅을 써본다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;함수형 인터페이스 (Functional Interface)&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8&lt;/a&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1624627866428&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Chapter&amp;nbsp;9.&amp;nbsp;Interfaces&quot; data-og-description=&quot;If a single field is inherited multiple times from the same interface because, for example, both this interface and one of this interface's direct superinterfaces extend the interface that declares the field, then only a single member results. This situati&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8&quot; data-og-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Chapter&amp;nbsp;9.&amp;nbsp;Interfaces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;If a single field is inherited multiple times from the same interface because, for example, both this interface and one of this interface's direct superinterfaces extend the interface that declares the field, then only a single member results. This situati&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Default 메소드 이외 단 한 개의 추상 메서드를 가지는 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- @FunctionalInterface 어노테이션이 붙은 인터페이스. @FunctionalInterface 어노테이션을 명시적으로 붙임으로써, 컴파일러에게는 해당 인터페이스가 함수형 인터페이스임을 알리고, 추상 메서드를 여러 개 작성하는 경우 또는 해당 인터페이스를 상속하는 경우 컴파일 단계에서 에러를 뱉게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에는 자바에서는 기본 데이터 타입들만 값으로 판단하였지만 (ex: Integer, int, Long, String), 함수형 인터페이스 등장으로 인해 함수도 값으로 판단할 수 있게 되었다. 함수 또한 값으로 판단하게 되어 유연한 코드 작성이 가능해졌다.&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;u&gt;&lt;b&gt;함수형 인터페이스 선언 예시&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624628422632&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.tistory.johnmark.functional;

@FunctionalInterface
public interface Validator {
	boolean match();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;함수형 인터페이스인데 추상 메소드가 한 개 이상인 경우 에러 발생&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624688634713&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.tistory.johnmark.functional;

@FunctionalInterface
public interface Validator {
	boolean match();
	void print();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;추상 메소드가 한 개라면 default 메소드나 static 메소드는 상관 없음&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624691421170&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.tistory.johnmark.functional;

@FunctionalInterface
public interface Validator {
	static void printClassName() {
		System.out.println(&quot;Validator&quot;);
	}

	default String getClassName() {
		return &quot;Validator&quot;;
	}

	boolean match();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;java8 부터는 interface에 default 키워드를 붙인 메소드를 선언할 수 있게 되었다. default 키워드가 붙은 메소드는 &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;u&gt;&lt;b&gt;활용 예시&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624628984721&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.tistory.johnmark.functional;

import java.util.Arrays;
import java.util.List;

public class FunctionalInterfaceTest {
	public static void main(String[] args) {
		List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;JohnMark&quot;, &quot;JeongHyeon&quot;, &quot;Bob&quot;, &quot;Mark&quot;, &quot;Richard&quot;, &quot;Narisa&quot;);
		List&amp;lt;Integer&amp;gt; scores = Arrays.asList(10, 20, 30, 40, 50, 60, 70, 80, 95, 100);

		for (String name : names) {
			// 함수형 인터페이스를 통해 함수자체를 값으로 넘김
			if (isValid(() -&amp;gt; name.startsWith(&quot;J&quot;))) {
				System.out.printf(&quot;Matched Name: [%s]\n&quot;, name);
			}
		}

		System.out.println(&quot;------------------------------------------&quot;);

		for (int score : scores) {
			// 함수형 인터페이스를 통해 함수자체를 값으로 넘김
			if (isValid(() -&amp;gt; score &amp;gt; 50)) {
				System.out.printf(&quot;Matched Score: [%d]\n&quot;, score);
			}
		}
	}

	public static boolean isValid(Validator validator) {
		return validator.match();
	}
}
&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;함수를 값으로 넘겨줄 수 있게 됨으로써, isValid 메소드의 인자인 함수형 인터페이스 Validator의 인스턴스를 람다 표현식으로 만들어 넘김으로 써, 함수형 인터페이스의 추상 메서드 내용을 String, Integer 등 다양한 데이터 타입에 대해 원하는 로직 (1. J로 시작하는 문자열, 2. 50보다 큰 숫자)을 유연하게 작성하고 적용할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;람다 표현식 (Lambda Expressions)&lt;br /&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27&lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1624686346134&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Chapter&amp;nbsp;15.&amp;nbsp;Expressions&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27&quot; data-og-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Chapter&amp;nbsp;15.&amp;nbsp;Expressions&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 람다 표현식은 메소드와 비슷함 (매개변수, 매개변수가 사용되는 body block)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 간결한 코드 작성 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 함수를 만드는 과정 없이 한 번에 처리 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 람다 표현식을 사용하면서 만드는 무명 함수는 재사용이 불가능함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 디버깅이 어려움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;형태&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;LambdaExpression:&lt;br /&gt;LambdaParameters&amp;nbsp;-&amp;gt;&amp;nbsp;LambdaBody&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;u&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624686978523&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;() -&amp;gt; {}                // No parameters; result is void
() -&amp;gt; 42                // No parameters, expression body
() -&amp;gt; null              // No parameters, expression body
() -&amp;gt; { return 42; }    // No parameters, block body with return
() -&amp;gt; { System.gc(); }  // No parameters, void block body

() -&amp;gt; {                 // Complex block body with returns
  if (true) return 12;
  else {
    int result = 15;
    for (int i = 1; i &amp;lt; 10; i++)
      result *= i;
    return result;
  }
}                          

(int x) -&amp;gt; x+1              // Single declared-type parameter
(int x) -&amp;gt; { return x+1; }  // Single declared-type parameter
(x) -&amp;gt; x+1                  // Single inferred-type parameter
x -&amp;gt; x+1                    // Parentheses optional for
                            // single inferred-type parameter

(String s) -&amp;gt; s.length()      // Single declared-type parameter
(Thread t) -&amp;gt; { t.start(); }  // Single declared-type parameter
s -&amp;gt; s.length()               // Single inferred-type parameter
t -&amp;gt; { t.start(); }           // Single inferred-type parameter

(int x, int y) -&amp;gt; x+y  // Multiple declared-type parameters
(x, y) -&amp;gt; x+y          // Multiple inferred-type parameters
(x, int y) -&amp;gt; x+y    // Illegal: can't mix inferred and declared types
(x, final y) -&amp;gt; x+y  // Illegal: no modifiers with inferred types
&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://docs.oracle.com/javase/specs/jls/se8/html/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/javase/specs/jls/se8/html/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624687010252&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;The Java&amp;reg; Language Specification&quot; data-og-description=&quot;James Gosling Bill Joy Guy Steele Gilad Bracha Alex Buckley&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/&quot; data-og-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/specs/jls/se8/html/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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 Java&amp;reg; Language Specification&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;James Gosling Bill Joy Guy Steele Gilad Bracha Alex Buckley&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.oracle.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>Programming/Java</category>
      <category>Functional Interface</category>
      <category>Java8</category>
      <category>Lambda</category>
      <category>람다표현식</category>
      <category>함수형인터페이스</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/144</guid>
      <comments>https://johnmarc.tistory.com/144#entry144comment</comments>
      <pubDate>Sat, 26 Jun 2021 14:57:48 +0900</pubDate>
    </item>
    <item>
      <title>객체, 클래스 그리고 인스턴스</title>
      <link>https://johnmarc.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;출퇴근 길이나, 자기 전에 가끔 okky나 velog 등등 개발자 커뮤니티를 한번 훑어보며, 오늘은 무슨 재밌는 이야깃거리가 없나 하고 보는 경우가 있다. 그러던 중 객체, 클래스, 인스턴스에 대한 정의가 무엇인지 물어보는 글이 있어서 곰곰이 생각해보다가 한번 정리를 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 프로그래밍은 실세계에서의 문제를 해결하기 위한 목적(?) 또는 방향성을 지니고 있고, 해당 문제를 어느 정도 추상화해서 다룬다. 이때 실세계에 있는 어떠한 것들을 객체지향에서는 객체라고 가리키고 있다고 생각한다. 예로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 학생부 프로그램을 만든다고 했을 때, 학생부에 기입되는 학생들은 하나의 객체이고, 학생부 또한 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;넓은 의미에서 객체는 우리가 인지를 하고 있는(식별 가능한) 유형, 무형 모든 것을 객체라고 생각할 수 있을 것 같다&lt;/b&gt;&lt;/u&gt;&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;추상화(Abstraction)&lt;/b&gt;&lt;/u&gt;를 하는 방법 중에는 &lt;b&gt;분류(Classification)&lt;/b&gt;와 &lt;b&gt;인스턴스화(Instantiation)&lt;/b&gt;라는 개념이 있다. &lt;u&gt;&lt;b&gt;분류(Classification)&lt;/b&gt;&lt;/u&gt;는 객체들의 공통적인 속성을 공유할 수 있는(또는 갖고 있는) 해당 범주(범위, 개념)를 의미하고, 이를 묶어 추상화하는 것을 의미한다. &lt;u&gt;&lt;b&gt;따라서 클래스란 객체의 속성을 묶어 추상화한 일종의 형식 또는 틀이라고 생각할 수 있을 것 같다&lt;/b&gt;&lt;/u&gt;&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인스턴스화(Instantiation)&lt;/b&gt;는 &lt;b&gt;분류(Classification)&lt;/b&gt;와는 다르게 &lt;u&gt;&lt;b&gt;속성의 범주나 대상의 개념을 실체화하여 객체로 만드는 과정을 의미한다.&lt;/b&gt;&lt;/u&gt; 위에서 클래스를 일종의 틀이라고 했다면, &lt;u&gt;&lt;b&gt;해당 틀을 이용해 실제로 무언가를 만드는 과정을 인스턴스화라고 하며 그 결과물을 인스턴스라고 생각한다&lt;/b&gt;&lt;/u&gt;&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ 3줄 요약 ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍을 하는 데 있어서 해결하고자 하는 문제와 관련해서 내가 인지를 하고 있는 것은 객체이고, 이를 적절한 공통 속성으로 모아 범주를 나는 것을 클래스, 공통적인 속성을 모아 범주를 나눈 것을 프로그래밍을 통해 실체화한 것을 인스턴스라고 생각하면 될 것 같다.&lt;/p&gt;</description>
      <category>Programming/Java</category>
      <category>class</category>
      <category>classification</category>
      <category>instance</category>
      <category>instantiation</category>
      <category>object</category>
      <category>객체</category>
      <category>객체지향</category>
      <category>뇌피셜</category>
      <category>인스턴스</category>
      <category>클래스</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/143</guid>
      <comments>https://johnmarc.tistory.com/143#entry143comment</comments>
      <pubDate>Tue, 15 Jun 2021 22:58:46 +0900</pubDate>
    </item>
    <item>
      <title>[Queue문제] - ZigZagOrder</title>
      <link>https://johnmarc.tistory.com/142</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;327&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k61ih/btq6zQ6EHLj/aqQqUC0I6ZzBQM0foUgRCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k61ih/btq6zQ6EHLj/aqQqUC0I6ZzBQM0foUgRCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k61ih/btq6zQ6EHLj/aqQqUC0I6ZzBQM0foUgRCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk61ih%2Fbtq6zQ6EHLj%2FaqQqUC0I6ZzBQM0foUgRCK%2Fimg.png&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;327&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 트리가 있을 때, [[1], [3, 2], [4, 5]] 의 결과를 출력하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트리를 탐색하는데, 한번씩 방문 순서를 지그 재그 형태로 바꿔가면서 탐색을 해야 하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 트리를 preorder(전위순회)로 순회하지만, L, R 부분을 바꿔가면서 탐색하도록 해야 한다.&lt;br /&gt;간단하게 boolean 타입 플래그를 두고, L와 R 노드를 방문하는 순서를 바꿔주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622953770405&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package inflearn.queue;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class TreeNode {
	public int val;
	public TreeNode left;
	public TreeNode right;

	public TreeNode(int val) {
		this.val = val;
	}
}

public class ZigZagTreeOrder {

	public static void main(String[] args) {
		TreeNode root = new TreeNode(1);
		root.left = new TreeNode(2);
		root.right = new TreeNode(3);
		root.left.left = new TreeNode(4);
		root.left.right = new TreeNode(5);

		ZigZagTreeOrder zigZagTreeOrder = new ZigZagTreeOrder();
		System.out.println(zigZagTreeOrder.solve(root));
	}

	public List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; solve(TreeNode root) {
		Queue&amp;lt;TreeNode&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
		List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
		boolean normalProcessing = true;
		queue.offer(root);

		while (!queue.isEmpty()) {
			List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
			int size = queue.size();

			for (int i = 0; i &amp;lt; size; i++) {
				TreeNode node = queue.poll();
				if (normalProcessing) {
					list.add(node.val);
				} else {
					list.add(0, node.val);
				}

				if (node.left != null) {
					queue.offer(node.left);
				}
				if (node.right != null) {
					queue.offer(node.right);
				}
			}
			normalProcessing = !normalProcessing;
			result.add(list);
		}

		return result;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Algorithm</category>
      <category>Algorithm</category>
      <category>BFS</category>
      <category>DFS</category>
      <category>inorder</category>
      <category>postorder</category>
      <category>preorder</category>
      <category>Queue</category>
      <category>Tree</category>
      <category>ZigZagOrder</category>
      <category>알고리즘</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/142</guid>
      <comments>https://johnmarc.tistory.com/142#entry142comment</comments>
      <pubDate>Sun, 6 Jun 2021 13:30:24 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] - [큐스택] 주식 가격 문제</title>
      <link>https://johnmarc.tistory.com/141</link>
      <description>&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://programmers.co.kr/learn/courses/30/lessons/42584&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42584&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622034550570&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 주식가격&quot; data-og-description=&quot;초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 return 하도록 solution 함수를 완성하세요. 제한사항 prices의 각 가격은 1 이상 10,00&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42584&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42584&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TN1eS/hyKmywsoAB/rAQnwukncys5CmF6tv9uj0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/y77ZF/hyKmzoAOmJ/qg1mkvsxfseaZu0kVtKuU0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42584&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42584&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TN1eS/hyKmywsoAB/rAQnwukncys5CmF6tv9uj0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/y77ZF/hyKmzoAOmJ/qg1mkvsxfseaZu0kVtKuU0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 주식가격&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 return 하도록 solution 함수를 완성하세요. 제한사항 prices의 각 가격은 1 이상 10,00&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[제약조건 및 풀이]&lt;/p&gt;
&lt;pre id=&quot;code_1622034557249&quot; class=&quot;java&quot; style=&quot;overflow: auto; margin: 0px; padding: 0px; color: #555555; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prices의 각 가격은 1 이상 10,000 이하인 자연수입니다.
prices의 길이는 2 이상 100,000 이하입니다.

prices [1, 2, 3, 2, 3]	
return [4, 3, 1, 1, 0]

1초 시점의 ₩1은 끝까지 가격이 떨어지지 않았습니다.
2초 시점의 ₩2은 끝까지 가격이 떨어지지 않았습니다.
3초 시점의 ₩3은 1초뒤에 가격이 떨어집니다. 따라서 1초간 가격이 떨어지지 않은 것으로 봅니다.
4초 시점의 ₩2은 1초간 가격이 떨어지지 않았습니다.
5초 시점의 ₩3은 0초간 가격이 떨어지지 않았습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐에 주식 가격 정보를 넣고, 큐에서 가격 정보를 꺼낸 뒤에(poll), 남아있는 큐를 기반으로 가격이 떨어질 때까지의 초를 계산했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1622034678332&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Queue;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.stream.IntStream;


class Solution {
    public int[] solution(int[] prices) {
		List&amp;lt;Integer&amp;gt; answer = new ArrayList&amp;lt;&amp;gt;();
		Queue&amp;lt;Integer&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();

		// save all price in queue
		for (int price : prices) {
			queue.offer(price);
		}

		// count
		int seconds = 0;

		while (!queue.isEmpty()) {
			if (queue.size() == 1) {
				answer.add(0);
				break;
			}

			int currentPrice = queue.poll();

			for (int value : queue) {
				seconds++;
				if (currentPrice &amp;gt; value) {
					break;
				}
			}
			answer.add(seconds);
			seconds = 0;
		}

		return answer.stream().flatMapToInt(IntStream::of).toArray(); 
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming/Algorithm</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/141</guid>
      <comments>https://johnmarc.tistory.com/141#entry141comment</comments>
      <pubDate>Wed, 26 May 2021 22:11:43 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] - [큐스택] 프린터 문제</title>
      <link>https://johnmarc.tistory.com/140</link>
      <description>&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://programmers.co.kr/learn/courses/30/lessons/42587&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42587&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1621951999617&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 프린터&quot; data-og-description=&quot;일반적인 프린터는 인쇄 요청이 들어온 순서대로 인쇄합니다. 그렇기 때문에 중요한 문서가 나중에 인쇄될 수 있습니다. 이런 문제를 보완하기 위해 중요도가 높은 문서를 먼저 인쇄하는 프린&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42587&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42587&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTsm1R/hyKld0JvE9/RBmQgHXPqQ6nvviiGQ6bOk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bwGMd4/hyKlbaL3wg/7bgWGnzvEYCZKnP9w5vSMk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42587&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42587&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTsm1R/hyKld0JvE9/RBmQgHXPqQ6nvviiGQ6bOk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bwGMd4/hyKlbaL3wg/7bgWGnzvEYCZKnP9w5vSMk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 프린터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 프린터는 인쇄 요청이 들어온 순서대로 인쇄합니다. 그렇기 때문에 중요한 문서가 나중에 인쇄될 수 있습니다. 이런 문제를 보완하기 위해 중요도가 높은 문서를 먼저 인쇄하는 프린&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[제약조건 및 풀이]&lt;/p&gt;
&lt;pre id=&quot;code_1621952032107&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 인쇄 대기목록의 가장 앞에 있는 문서(J)를 대기목록에서 꺼냅니다.
2. 나머지 인쇄 대기목록에서 J보다 중요도가 높은 문서가 한 개라도 존재하면 J를 대기목록의 가장 마지막에 넣습니다.
3. 그렇지 않으면 J를 인쇄합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이는.. 그냥 문제 설명대로 풀었다. 핵심은 원하는 값이 나올 때까지 위 과정을 1~2~3 과정을 반복해야 한다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1621952145594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Queue;
import java.util.LinkedList;
class Solution {
	public int solution(int[] priorities, int location) {
		Queue&amp;lt;Integer&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
		int count = 1;
		for (int i = 0; i &amp;lt; priorities.length; i++) {
			queue.offer(i);
		}
		while (true) {
			int target = queue.poll();
			if (isBiggerExist(target, queue, priorities)) {
				queue.offer(target);
			} else {
				if (target == location) {
					return count;
				} else {
					count++;
				}
			}
		}
	}
	private boolean isBiggerExist(int target, Queue&amp;lt;Integer&amp;gt; compare, int[] priorites) {
		for (int value : compare) {
			if (priorites[target] &amp;lt; priorites[value]) {
				return true;
			}
		}
		return false;
	}
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming/Algorithm</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/140</guid>
      <comments>https://johnmarc.tistory.com/140#entry140comment</comments>
      <pubDate>Tue, 25 May 2021 23:16:22 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] - [큐스택] 기능개발 문제</title>
      <link>https://johnmarc.tistory.com/139</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42586&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1621747210592&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 기능개발&quot; data-og-description=&quot;프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다. 또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iU3xS/hyKjCllmxa/z7f84svo49VVf8SGI2Rslk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/cVw2hO/hyKia43Tvq/F3ScqBbmnhWwYeugPYCDCK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iU3xS/hyKjCllmxa/z7f84svo49VVf8SGI2Rslk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/cVw2hO/hyKia43Tvq/F3ScqBbmnhWwYeugPYCDCK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 기능개발&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다. 또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1621747156567&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.ArrayList;
import java.util.Queue;
import java.util.stream.IntStream;
import java.util.concurrent.ConcurrentLinkedQueue;


class Solution {
   	public int[] solution(int[] progresses, int[] speeds) {
		Queue&amp;lt;Integer&amp;gt; deployQueue = new ConcurrentLinkedQueue&amp;lt;&amp;gt;();

		for (int i = 0; i &amp;lt; progresses.length; i++) {
			deployQueue.add(getWorkingDay(progresses[i], speeds[i]));
		}

		ArrayList&amp;lt;Integer&amp;gt; answerList = new ArrayList&amp;lt;&amp;gt;();
		int first = deployQueue.poll();
		int count = 1;
		while (!deployQueue.isEmpty()) {
			int next = deployQueue.poll();
			if (first &amp;gt;= next) {
				count++;
			} else {
				answerList.add(count);
				count = 1;
				first = next;
			}
		}
		answerList.add(count);
        return answerList.stream().flatMapToInt(IntStream::of).toArray();
	}

	public int getWorkingDay(int progress, int speed) {
		int quotient = (100 - progress) / speed;
		int remain = (100 - progress) % speed;
		if (remain == 0) {
			return quotient;
		}
		return quotient + 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이 아이디어는 간단했다. 배포의 순서는 반드시 보장되어야 한다는 조건이 있다. ex) 앞 기능의 배포보다 뒤 기능 개발이 빨리 개발되는 경우는 존재하나 이는 앞 기능이 배포될 때 같이 배포된다. 따라서 순서를 보장하는 자료구조인 큐를 사용했고, 진척률과 작업 속도를 바탕으로 해당 기능의 구현 기간을 구한 뒤, 이 값을 바탕으로 배포일 별 기능 수를 구했다.&lt;/p&gt;</description>
      <category>Programming/Algorithm</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/139</guid>
      <comments>https://johnmarc.tistory.com/139#entry139comment</comments>
      <pubDate>Sun, 23 May 2021 14:24:24 +0900</pubDate>
    </item>
    <item>
      <title>Go로 초간단 CORS Proxy 서버 만들기</title>
      <link>https://johnmarc.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;웹 프론트 프로젝트를 할 때 호스트가 다른 서버로 API를 요청하는 경우 CORS 오류를 많이 겪어봤을 것이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이를 위해 중간에 CORS Proxy 서버를 두고 테스트를 하는 경우가 많은데 node로 만들어져 npm에 등록된 프로젝트도 많이 보였다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Go를 조금씩 공부하고 있어서 Go의 httpuil 패키지의 reverse proxy 모듈을 활용하여 간단한 CORS Proxy 서버를 만들어보았다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;타겟 서버가 세션 쿠키를 사용하는 경우를 대비해서, 쿠키 내용을 파싱하여 호스트 정보를 바꾸는 부분 또한 추가했으며 타겟 서버의 주소와 access control allow origin에 들어갈 현재 클라이언트의 호스트 정보, 프로그램의 포트 정보 등 부가 정보는 .env파일로 따로 작성하여 불러오도록 하였다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;br&gt;.env 파일 구조는 다음과 같다.&lt;/p&gt;&lt;pre data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;# Reverse Proxy Target Host Information
TARGET_HOST=
# This Proxy Server Port
LOCAL_PORT=
# CORS Allow Origin Information. It is Client host information (i.e https://192.168.93.1:4000)
ACCESS_CONTROL_ALLOWS_ORIGIN=
# Credentials Flag for Sharing Cookie between origin resource server, Must Set as true
WITH_CREDENTIALS=
# Optional, Session Cookie Name In Resource Server (If, not using cookie, let that empty)
SESSION_COOKIE_NAME=
# SSL Key File Path For Running Proxy Server as Https
SSL_KEY_PATH=
# SSL Cert File Path For Running Proxy Server as Https
SSL_CERT_PATH=&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;리버스 프록시를 통해 cors 기능을 제공하는 go 코드는 다음과 같다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;package main

import (
	&quot;crypto/tls&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;net/http/httputil&quot;
	&quot;net/url&quot;
	&quot;os&quot;
	&quot;strings&quot;

	&quot;github.com/joho/godotenv&quot;
)

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal(&quot;Error Loading .env File&quot;)
	}

	remote, err := url.Parse(os.Getenv(&quot;TARGET_HOST&quot;))
	if err != nil {
		panic(err)
	}

	log.Printf(&quot;Forwarding Target : %s://%s&quot;, remote.Scheme, remote.Host)

	proxy := httputil.NewSingleHostReverseProxy(remote)
	proxy.ModifyResponse = corsHeaderModify

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &amp;amp;tls.Config{InsecureSkipVerify: true}

	http.HandleFunc(&quot;/&quot;, handler(proxy))

	err = http.ListenAndServeTLS(&quot;:&quot;+os.Getenv(&quot;LOCAL_PORT&quot;), os.Getenv(&quot;SSL_CERT_PATH&quot;), os.Getenv(&quot;SSL_KEY_PATH&quot;), nil)
	if err != nil {
		panic(err)
	}
}

func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
	return func(resp http.ResponseWriter, r *http.Request) {
		//  If, current request is pre-flight
		if r.Method == &quot;OPTIONS&quot; {
			resp.Header().Set(&quot;Access-Control-Allow-Origin&quot;, os.Getenv(&quot;ACCESS_CONTROL_ALLOWS_ORIGIN&quot;))
			resp.Header().Set(&quot;Access-Control-Allow-Headers&quot;, &quot;Access-Control-Allow-Origin, content-type&quot;)
			resp.Header().Set(&quot;Access-Control-Allow-Methods&quot;, &quot;*&quot;)
			resp.Header().Set(&quot;Access-Control-Expose-Headers&quot;, &quot;Set-Cookie, Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Credential, Authorization&quot;)
			resp.Header().Set(&quot;Vary&quot;, &quot;Origin&quot;)
			resp.Header().Set(&quot;Vary&quot;, &quot;Access-Control-Request-Method&quot;)
			resp.Header().Set(&quot;Vary&quot;, &quot;Access-Control-Request-Headers&quot;)
			resp.Header().Set(&quot;Access-Control-Allow-Credentials&quot;, &quot;true&quot;)
			return
		} else {
			log.Printf(&quot;%s %s -&amp;gt; Cros_Proxy -&amp;gt; %s&quot;, r.Method, r.Host+r.URL.RequestURI(), os.Getenv(&quot;TARGET_HOST&quot;)+r.URL.RequestURI())
			p.ServeHTTP(resp, r)
		}
	}
}

func corsHeaderModify(resp *http.Response) error {
	// Set Basic Cors related header
	resp.Header.Set(&quot;Access-Control-Allow-Origin&quot;, os.Getenv(&quot;ACCESS_CONTROL_ALLOWS_ORIGIN&quot;))
	resp.Header.Set(&quot;Access-Control-Allow-Headers&quot;, &quot;Access-Control-Allow-Origin, content-type&quot;)
	resp.Header.Set(&quot;Access-Control-Allow-Methods&quot;, &quot;*&quot;)
	resp.Header.Set(&quot;Access-Control-Expose-Headers&quot;, &quot;Set-Cookie, Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Credential, Authorization&quot;)
	resp.Header.Set(&quot;Vary&quot;, &quot;Origin&quot;)
	resp.Header.Set(&quot;Vary&quot;, &quot;Access-Control-Request-Method&quot;)
	resp.Header.Set(&quot;Vary&quot;, &quot;Access-Control-Request-Headers&quot;)
	resp.Header.Set(&quot;Access-Control-Allow-Credentials&quot;, &quot;true&quot;)

	// Parsing cookie in header
	for _, value := range strings.Split(resp.Header.Get(&quot;Set-Cookie&quot;), &quot;;&quot;) {
		// If remove the domain value, the client host information is automatically set to the domain value by the browser.
		if strings.Contains(value, &quot;Domain=&quot;) {
			var newCookie = strings.Replace(resp.Header.Get(&quot;Set-Cookie&quot;), value, &quot;&quot;, 1)
			resp.Header.Set(&quot;Set-Cookie&quot;, newCookie)
		}
	}
	return nil
}&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;심플하게 다음과 같이 프로그램을 실행시킬 수 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;go run main.go&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;깃헙&lt;/b&gt; &lt;a href=&quot;https://github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY&quot; target=&quot;_blank&quot;&gt;github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;LoveAndCode/SIMPLE_CORS_GO_PROXY&quot; data-og-description=&quot;Simple CORS Proxy Server by Go . Contribute to LoveAndCode/SIMPLE_CORS_GO_PROXY development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qtk6I/hyJXVyFW1A/V4ysRWXGzgw2it2SsDdMA1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY&quot;&gt;&lt;a href=&quot;https://github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY&quot; target=&quot;_blank&quot; data-source-url=&quot;https://github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image:url(https://scrap.kakaocdn.net/dn/qtk6I/hyJXVyFW1A/V4ysRWXGzgw2it2SsDdMA1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;LoveAndCode/SIMPLE_CORS_GO_PROXY&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;Simple CORS Proxy Server by Go . Contribute to LoveAndCode/SIMPLE_CORS_GO_PROXY development by creating an account on GitHub.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Go</category>
      <category>cors</category>
      <category>CORS Go</category>
      <category>CORS Go Proxy</category>
      <category>CORS Proxy</category>
      <category>CORS 세션쿠키</category>
      <category>CORS 프록시 서버</category>
      <category>Go CORS</category>
      <category>Revese Proxy</category>
      <category>Session</category>
      <category>세션쿠키</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/138</guid>
      <comments>https://johnmarc.tistory.com/138#entry138comment</comments>
      <pubDate>Wed, 14 Apr 2021 22:18:40 +0900</pubDate>
    </item>
    <item>
      <title>UTF8 with BOM 으로 파일 인코딩해서 저장하기</title>
      <link>https://johnmarc.tistory.com/137</link>
      <description>&lt;p&gt;최근 만들고 있는 자막 변환기 파이썬 프로그램을 통해 .srt 파일을 만드는 도중, subtitle editor에서 저장된 .srt 파일이 UTF8 with BOM 형태로 인코딩 되어 저장되는 사실을 알았다. &lt;br /&gt;어도비 프리미어 프로에서 내가만든 파이썬 프로그램의 .srt 파일 결과물이 인식이 안되어 혹시 해당 인코딩 문제인 줄 알고 인코딩을 바꿔본 경험을 적어본다 (프리미어 프로에 .srt 파일이 인식 안되던 건 사실 인코딩 문제가 아니었다).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618405204076&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;out_put1 = open(os.path.join(save_path_info, file_name + &quot;_자막.srt&quot;), &quot;w&quot;, encoding='utf8')
''' 해당 특문을 파일 최상단에 먼저 적어줘야 utf8 with bom으로 인식한다. 실 파일에는 해당 내용은 표시 되지 않는다 '''
out_put1.write(u'\ufeff')
out_put1.write('1\n');
out_put1.write('00:00:00:00 --&amp;gt; 00:00:00:00\n')
out_put1.write('야호~~ 이건 utf8 with bom encoding 된 파일이다~\n\n')
out_put1.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 파일을 작성하고 저장하면 다음과 같이 UTF8 with BOM 인코딩으로 잘 잡힌다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HIu1K/btq2yag3gKq/wIAqcvNZBbEhbXqNiHfUCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HIu1K/btq2yag3gKq/wIAqcvNZBbEhbXqNiHfUCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HIu1K/btq2yag3gKq/wIAqcvNZBbEhbXqNiHfUCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHIu1K%2Fbtq2yag3gKq%2FwIAqcvNZBbEhbXqNiHfUCk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>Python</category>
      <category>python .srt</category>
      <category>python encoding</category>
      <category>python utf8 with bom</category>
      <category>python3 file encoding</category>
      <category>utf8 with BOM</category>
      <category>파이썬 UTF8 with BOM</category>
      <category>파이썬 utf8 with bom 파일 만들기</category>
      <category>파이썬 파일 인코딩</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/137</guid>
      <comments>https://johnmarc.tistory.com/137#entry137comment</comments>
      <pubDate>Wed, 14 Apr 2021 22:11:26 +0900</pubDate>
    </item>
    <item>
      <title>Dictionary 데이터에서 키 값만 추출하기</title>
      <link>https://johnmarc.tistory.com/136</link>
      <description>&lt;p&gt;최근 지인 부탁으로 파이썬으로 자막을 변환하는 gui 프로그램을 만들고 있는데, 그러다가 알게 된 사실을 메모해본다.&lt;/p&gt;
&lt;p&gt;파이썬을 제대로 깊게 공부해본적은 없고 목적에 따라 필요한 부분만 배워서 쓰고 있다. 참고로 파이썬3를 쓰고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;dictionary 객체에 keys() 함수를 호출한 것을 list()로 감싸주면 키 값만 담긴 리스트가 생긴다. 이후 적절한 인덱스 값을 통해 키값을 가져오면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DFiD1/btq2DE8tNOH/FZNueoUyWyRRkWkNrBOSuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DFiD1/btq2DE8tNOH/FZNueoUyWyRRkWkNrBOSuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DFiD1/btq2DE8tNOH/FZNueoUyWyRRkWkNrBOSuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDFiD1%2Fbtq2DE8tNOH%2FFZNueoUyWyRRkWkNrBOSuk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>Dictionary</category>
      <category>Python</category>
      <category>python dictionary key</category>
      <category>python dictionary to list</category>
      <category>python3</category>
      <category>딕셔너리 키</category>
      <category>파이썬</category>
      <category>파이썬 딕셔너리</category>
      <category>파이썬 딕셔너리 키 추출</category>
      <category>파이썬3</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/136</guid>
      <comments>https://johnmarc.tistory.com/136#entry136comment</comments>
      <pubDate>Wed, 14 Apr 2021 21:54:44 +0900</pubDate>
    </item>
    <item>
      <title>[AWS, SSH] Jump Host, Bastion Server 경유해서 다른 서버 접속</title>
      <link>https://johnmarc.tistory.com/135</link>
      <description>&lt;p&gt;회사에서는 aws를 통해 서비스를 배포 운영하고 있는데, 실제 서비스들은 외부에서 접근이 불가능한 private subnet에 위치해있다. 그리고 외부에서 이 서비스를 접근하기 위해서 반드시 거쳐가야 하는 일종의 게이트웨이 역할을 하는 서버가 있는데 바로 베스천 서버이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;bastion_diagram.png&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;380&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QHghe/btq2h2PJosI/vfsjL3GAx4EskhcKiwxKb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QHghe/btq2h2PJosI/vfsjL3GAx4EskhcKiwxKb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QHghe/btq2h2PJosI/vfsjL3GAx4EskhcKiwxKb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQHghe%2Fbtq2h2PJosI%2FvfsjL3GAx4EskhcKiwxKb0%2Fimg.png&quot; data-filename=&quot;bastion_diagram.png&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;380&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;베스천 서버는 public subnet에 위치하며, 여러 보안적인 이슈를 담당한다. 특정 IP만 특정 subnet에 접근가능하도록 강제를 하거나, 서버 접근 이력 정보를 남기는 등 보안적으로 많은 이점이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;베스천 서버를 통해 실제 서버로 접속을 하려면 ssh 터널링을 통해 로컬 호스트에 포트를 띄우고 해당 포트로 접근을 해야했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618026893369&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh -fnNT -L &amp;lt;로컬에 띄울 프록시 포트&amp;gt;:&amp;lt;private subnet에 위치한 서버 IP&amp;gt;:22  &amp;lt;SSH 접속 계정&amp;gt;@&amp;lt;베스천 서버 IP&amp;gt; -i &amp;lt;인증서 키파일&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 타겟 서버에 맞게 입력을 해보면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOmsU/btq2idwJwaA/yNcbkG6GuLLWPwzXtSnKNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOmsU/btq2idwJwaA/yNcbkG6GuLLWPwzXtSnKNk/img.png&quot; data-alt=&quot;bastion을 오타 쳐서 bestion_id_rsa라고 잘못 키파일 명을 만들었지만 귀찮아서 그냥 쓰고 있다..ㅋㅋㅋㅋ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOmsU/btq2idwJwaA/yNcbkG6GuLLWPwzXtSnKNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOmsU%2Fbtq2idwJwaA%2FyNcbkG6GuLLWPwzXtSnKNk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bastion을 오타 쳐서 bestion_id_rsa라고 잘못 키파일 명을 만들었지만 귀찮아서 그냥 쓰고 있다..ㅋㅋㅋㅋ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;아래와 같이 성공적으로 ssh 프록시 포트가 열린 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0CCcI/btq2gX2EW9W/vmByokDdbbnMeGcpqXscDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0CCcI/btq2gX2EW9W/vmByokDdbbnMeGcpqXscDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0CCcI/btq2gX2EW9W/vmByokDdbbnMeGcpqXscDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0CCcI%2Fbtq2gX2EW9W%2FvmByokDdbbnMeGcpqXscDK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 해당 포트로 ssh 접속을 하면 private net에 위치한 서버를 베스천 서버를 통해 접근하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618027545128&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh &amp;lt;SSH 접속 계정&amp;gt;@localhost -p &amp;lt;프록시 포트&amp;gt; -i &amp;lt;인증 키 파일&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biwRAG/btq2gxQQz9k/6fYdUmSdCzI4xEuHKDGxB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biwRAG/btq2gxQQz9k/6fYdUmSdCzI4xEuHKDGxB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biwRAG/btq2gxQQz9k/6fYdUmSdCzI4xEuHKDGxB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiwRAG%2Fbtq2gxQQz9k%2F6fYdUmSdCzI4xEuHKDGxB1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Laihy/btq2ksfQOeF/VnM7JlQxNbCZ2LxPTjkCxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Laihy/btq2ksfQOeF/VnM7JlQxNbCZ2LxPTjkCxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Laihy/btq2ksfQOeF/VnM7JlQxNbCZ2LxPTjkCxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLaihy%2Fbtq2ksfQOeF%2FVnM7JlQxNbCZ2LxPTjkCxk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 하면 ssh 명령어를 통해 베스천 서버를 통해 private subnet에 위치한 서버로 ssh 연결을 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 단점이 있는데, 접근할 일이 있을 때마다 터널링 포트를 띄우고, 해당 포트로 ssh 연결을 해야 한다는 점이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 점은 ~/.ssh/config 파일을 적극 활용하면 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가장 먼저 ssh 연결 시 사용했던 인증 키파일을 ~/.ssh/하위에 옮긴다.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618028298645&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cp &amp;lt;ssh 접속 키파일&amp;gt; ~/.ssh&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그다음 아래 명령어를 통해 ~/.ssh/config 파일을 열자. 없다면 만들어도 좋다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618028171306&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vim ~/.ssh/config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 다음 아래와 같이 입력한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618028430676&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Host bastion
  HostName &amp;lt;bastion 서버 IP&amp;gt;
  User &amp;lt;SSH 접속 계정&amp;gt;
  ForwardAgent yes
  IdentityFile &amp;lt;베스천 서버 접속 ssh 키파일 절대 경로&amp;gt;

Host staging &amp;lt;ssh 접속 호스트 명, 임의대로 지정해도 좋다&amp;gt;
  HostName &amp;lt;private subnet에 위치한 타겟 서버 IP&amp;gt;
  User &amp;lt;SSH 접속 계정&amp;gt;
  IdentityFile &amp;lt;베스천 서버 접속 ssh 키파일 절대 경로&amp;gt;
  ProxyCommand ssh bastion -W %h:%p&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 작성한 다음 저장한 후, 아래와 같이 명령어를 실행하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1618028607122&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh staging&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전에는 접속하기 위해 터널링 포트를 띄우고 다시 해당 포트로 ssh 연결하던 단계가 ssh 명령어 하나로 줄어들었다.&lt;/p&gt;
&lt;p&gt;실제로 명령어를 실행하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6c0ZN/btq2hrbnn1h/aOj8bXZ95nTenPkhkWGgcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6c0ZN/btq2hrbnn1h/aOj8bXZ95nTenPkhkWGgcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6c0ZN/btq2hrbnn1h/aOj8bXZ95nTenPkhkWGgcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6c0ZN%2Fbtq2hrbnn1h%2FaOj8bXZ95nTenPkhkWGgcK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 명령어를 실행하면, 베스천 서버로 연결한 뒤에 다시 private subnet에 위치한 서버로 proxy된다. 나 같은 경우 접속 인증 키파일에 passphrase가 설정되어있어서 베스천 한번, 타겟 서버 한번 이렇게 총 두 번 비밀번호를 입력을 해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 너무 간편해졌다. 동일한 비밀번호를 두 번 정도 입력하는 거야 큰 부담이 되진 않았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;몇 달을 처음 제시한 방법처럼 터널링 포트를 띄우고 접속을 했었는데, 지금은 퇴사하신 시니어 서버 개발자님의 도움으로 꿀팁을 전수받았다. 나와 같은 고통을 겪는 사람들에게 많은 도움이 되었으면 좋겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Server</category>
      <category>AWS</category>
      <category>Bastion Host</category>
      <category>bastion ssh 접속</category>
      <category>private subnet</category>
      <category>Proxy Command</category>
      <category>public subnet</category>
      <category>ssh</category>
      <category>SSH Proxy</category>
      <category>SSH 터널링</category>
      <category>베스천 서버</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/135</guid>
      <comments>https://johnmarc.tistory.com/135#entry135comment</comments>
      <pubDate>Sat, 10 Apr 2021 13:36:13 +0900</pubDate>
    </item>
    <item>
      <title>Intellij 단축키 꿀팁</title>
      <link>https://johnmarc.tistory.com/134</link>
      <description>&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Generate (&lt;strong&gt;Ctrl + Insert&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;→ 소스 에디터에서는 getter, setter, override, 등등 여러 가지를 생성 가능하도록 도와줌&lt;/p&gt;
&lt;p&gt;→ 프로젝트 창에서는 class, enum 등 여러 가지 파일을 생성 가능하도록 도와줌&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete Line ( Ctrl + Y )&lt;/p&gt;
&lt;p&gt;→ 현재 커서가 위치한 줄을 삭제함, shitf + 방향키 (위, 아래)로 영역을 잡고 누르면 영역 단위로 삭제됨&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Duplicate Line ( Ctrl + D )&lt;/p&gt;
&lt;p&gt;→ 현재 커서가 위치한 줄을 복제함, shift+ 방향키 (위, 아래)로 영역을 잡고 누르면&lt;/p&gt;
&lt;p&gt;영역 단위로 복제됨&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Action ( Alt + Enter )&lt;/p&gt;
&lt;p&gt;→ 상황에 맞추어 액션을 추천해줌 (삭제, 코드 최적화, 오류 찾기 등..)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oai0U/btqUZus4TdO/JlUx9gfVIPXYxFCnwO2cRk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oai0U/btqUZus4TdO/JlUx9gfVIPXYxFCnwO2cRk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oai0U/btqUZus4TdO/JlUx9gfVIPXYxFCnwO2cRk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/oai0U/btqUZus4TdO/JlUx9gfVIPXYxFCnwO2cRk/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Error Code Jump (F2)&lt;/p&gt;
&lt;p&gt;→ 에러, 오류, 경고, 제안이 발생하는 코드 라인으로 점프를 합니다. 긴 코드 속에서 문제 될만한 코드라인 잡을 때 개꿀입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Project 창 Toggle ( Alt + 1)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 프로젝트 창을 열었다, 닫았다, 화면 전환할 수 있는 단축키입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NcLHl/btqUWtIozYs/7y9Kn38MKii977F4A45dw1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NcLHl/btqUWtIozYs/7y9Kn38MKii977F4A45dw1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NcLHl/btqUWtIozYs/7y9Kn38MKii977F4A45dw1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/NcLHl/btqUWtIozYs/7y9Kn38MKii977F4A45dw1/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;최근에 작업한 파일 보기 ( Ctrl + E )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 내가 최근에 작업했었던 파일들을 변경순으로 정렬해서 모달 창으로 보여줍니다.&lt;/p&gt;
&lt;p&gt;→ 작업 전환이나, 내가 방금 전에 뭘 작업했었는지 확인하기 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1v5Aa/btqUZtOrIBC/rKl1eCayrYsdstpp7VAwHK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1v5Aa/btqUZtOrIBC/rKl1eCayrYsdstpp7VAwHK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1v5Aa/btqUZtOrIBC/rKl1eCayrYsdstpp7VAwHK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/c1v5Aa/btqUZtOrIBC/rKl1eCayrYsdstpp7VAwHK/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;심벌 이동 ( Ctrl + B )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 소스코드 상에서 변수에 커서를 두고 Ctrl + B를 누르면 해당 변수가 선언된 곳으로 이동합니다.&lt;/p&gt;
&lt;p&gt;→ 소스코드 상에서 클래스 이름 위에 커서를 두고 Ctrl + B를 누르면 해당 클래스의 파일로 이동합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIWk5R/btqUYGURvi0/kC9kZ9XVvkcriKUTdJQki1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIWk5R/btqUYGURvi0/kC9kZ9XVvkcriKUTdJQki1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIWk5R/btqUYGURvi0/kC9kZ9XVvkcriKUTdJQki1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bIWk5R/btqUYGURvi0/kC9kZ9XVvkcriKUTdJQki1/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사용된 곳 찾기 ( Alt + F7 )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 클래스나 변수 등 어떤 항목을 수정하기 전에 해당 항목이 어느 위치에서 사용되었는지 또는 그냥 해당 항목이 어느 위치에서 어떻게 사용되었는지 확인할 수 있습니다. 사용된 모든 곳이 검색창에 표시가 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKxl74/btqU4CcJaJy/06llVT8U4SYm85kq7Kpb40/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKxl74/btqU4CcJaJy/06llVT8U4SYm85kq7Kpb40/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKxl74/btqU4CcJaJy/06llVT8U4SYm85kq7Kpb40/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bKxl74/btqU4CcJaJy/06llVT8U4SYm85kq7Kpb40/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;실행시켜버리기 ( Ctrl + Ctrl )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 어디서든 Ctrl 키를 두 번 눌러서 어떤 항목이든 실행할 수 있습니다. Run Anything 창이 열리고 해당 화면에서 실행할 기본 목록이 표시되지만 검색을 통해서 다른 실행 구성도 확인 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTDbaO/btqU4ZS4Axt/AZWLcpukK5dMkkCUxhTp6K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTDbaO/btqU4ZS4Axt/AZWLcpukK5dMkkCUxhTp6K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTDbaO/btqU4ZS4Axt/AZWLcpukK5dMkkCUxhTp6K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bTDbaO/btqU4ZS4Axt/AZWLcpukK5dMkkCUxhTp6K/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;주석 치기 ( Ctrl + / )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 커서가 위치한 해당 줄이 주석으로 변경됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/or4jW/btqUZt1W5eq/scHSD0rMrQCoHaE1TAuCYk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/or4jW/btqUZt1W5eq/scHSD0rMrQCoHaE1TAuCYk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/or4jW/btqUZt1W5eq/scHSD0rMrQCoHaE1TAuCYk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/or4jW/btqUZt1W5eq/scHSD0rMrQCoHaE1TAuCYk/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;리팩토링 ( Shift + Ctrl + Alt + T )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 심벌 또는 영역 위에 단축키를 누르면 사용가능한 리팩토링 옵션들이 표시됩니다. 리팩토링하기 아주 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vcTWn/btqUYGHi9Jx/LkOHG8fSyCQwMgKpTN6uhk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vcTWn/btqUYGHi9Jx/LkOHG8fSyCQwMgKpTN6uhk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vcTWn/btqUYGHi9Jx/LkOHG8fSyCQwMgKpTN6uhk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/vcTWn/btqUYGHi9Jx/LkOHG8fSyCQwMgKpTN6uhk/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;액션 검색 ( Ctrl + Shift + A )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 모든 단축키에 해당하는 액션들을 검색할 수 있는 검색창이 표시됩니다. 완전 유용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLkV6E/btqU0xwjHRM/tL2W05yrafG8PE5aj88kjk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLkV6E/btqU0xwjHRM/tL2W05yrafG8PE5aj88kjk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLkV6E/btqU0xwjHRM/tL2W05yrafG8PE5aj88kjk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cLkV6E/btqU0xwjHRM/tL2W05yrafG8PE5aj88kjk/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;전체 검색 ( Shift + Shift )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ Shift를 두 번 연타하면, IDE의 모든 걸 검색할 수 있는 검색창이 표시됩니다. Class 파일을 찾거나, method를 찾거나, 설정 옵션을 찾거나, 액션을 찾거나 뭐든 다 검색이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/neCs7/btqUZumeP1L/aUHUPPnvrBclKuOeO0WIhK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/neCs7/btqUZumeP1L/aUHUPPnvrBclKuOeO0WIhK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/neCs7/btqUZumeP1L/aUHUPPnvrBclKuOeO0WIhK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/neCs7/btqUZumeP1L/aUHUPPnvrBclKuOeO0WIhK/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;최근 내가 작업한 코드 위치 검색 ( Ctrl + Shift + E )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 최근에 내가 작업했던 영역 정보를 목록으로 보여줍니다. Ctrl + E와 다르게 파일 안에 위치까지 보여줍니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;인터페이스 메서드 구현 ( Ctrl + I )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 구현할 인터페이스의 메서드 정보를 보여주고 선택 가능하도록 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;메서드 오버 라이딩 구현 ( Ctrl + O )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 현재 클래스에서 오버 라이딩이 가능한 메서드 목록 정보를 보여주고 선택 가능하도록 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;변수 선언하고 만들어주기 ( Ctrl + Alt + V )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 변수 할당 구문을 자동으로 만들어줍니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;현재 구문 완성시키기 ( Shift + Ctrl + Enter ) → 현재 구문에서 미완성되는 부분 (예로 세미콜론이 없는 경우)을 자동으로 완성시켜줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zhxDY/btqU7bS2kPY/FohSdKhcElexqjbL5lQclk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zhxDY/btqU7bS2kPY/FohSdKhcElexqjbL5lQclk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zhxDY/btqU7bS2kPY/FohSdKhcElexqjbL5lQclk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zhxDY/btqU7bS2kPY/FohSdKhcElexqjbL5lQclk/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;각종 sout&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;sout 치고 엔터를 치면 System.out.Println()&lt;/p&gt;
&lt;p&gt;soutv 치고 엔터를 치면 현재 영역에 존재하는 변수를 선택할 수 있고, 변수를 선택하면 해당 변수를 출력해주는 구문을 완성시켜줍니다.&lt;/p&gt;
&lt;p&gt;soutp 치고 엔터를 치면 현재 함수 영역에 있는 파라미터를 선택할 수 있고, 선택한 파라미터를 출력해주는 구문을 완성시켜줍니다.&lt;/p&gt;
&lt;p&gt;soutm 치고 엔터를 치면 현재 클래스의 현재 메소드 정보를 출력해주는 구문을 완성시켜줍니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;터미널 창 켰다 껐다 전환하기 ( Alt + F12 )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ IDE에 내장된 터미널 창을 열었다가 닫았다가 할 수 있습니다. 작업을 하다가 터미널에서 작업이 필요한 경우 유용합니다 (EX : git add , git commit , git push... etc).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;테스트 클래스 자동으로 만들어 주기 ( Ctrl + Shift + T )&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;→ 현재 클래스 또는 소스 파일에 해당하는 테스트 클래스를 만들어주는 화면이 뜹니다. 적절하게 설정한 뒤에 확인을 누르면, test 디렉토리 하위에 이에 맞는 테스트 클래스가 생성됩니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;소스 코드 에디터 창에서 탭 이동 ( Alt + 방향키 (← or →) ) → 소스코드 에디터 탭 여러 개 떠 있고, 창 이동할 때 유용합니다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;[사진 출처]&lt;br&gt;&lt;a href=&quot;https://blog.jetbrains.com/ko/2020/03/11/top-15-intellij-idea-shortcuts_ko/&quot;&gt;blog.jetbrains.com/ko/2020/03/11/top-15-intellij-idea-shortcuts_ko/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming/ETC</category>
      <category>intellij IDEA</category>
      <category>IntelliJ Shortcut</category>
      <category>IntelliJ 단축키</category>
      <category>단축키</category>
      <category>인텔리제이 단축키</category>
      <category>코드 자동완성</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/134</guid>
      <comments>https://johnmarc.tistory.com/134#entry134comment</comments>
      <pubDate>Thu, 28 Jan 2021 22:47:59 +0900</pubDate>
    </item>
    <item>
      <title>[SpringBoot] Spring Boot 2.3.8 도커 컨테이너 만들기 (BuildPack)</title>
      <link>https://johnmarc.tistory.com/133</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Spring Boot 2.3 버전부터 내장 애플리케이션을 Docker Image로 만드는 라이브러리가 포함되어 배포되었다. 내 기억에&amp;nbsp; 초기에는 Jib를 사용했었던 것 같은데, 2.3.8 버전을 사용해보니 BuildPack을 사용하도록 변경되어있었다. BuildPack에 대한 내용은 아래 사이트를 참고하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://buildpacks.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;buildpacks.io/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1611321243043&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Cloud Native Buildpacks&quot; data-og-description=&quot;Cloud Native Buildpacks transform your application source code into images that can run on any cloud.&quot; data-og-host=&quot;buildpacks.io&quot; data-og-source-url=&quot;https://buildpacks.io/&quot; data-og-url=&quot;https://buildpacks.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cZ1vU8/hyI2zcf2K2/gKgT0is9HxQVfRFoZKBnTK/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/fYgt5/hyI2FDxPha/wKGF9CHNNkjUfqcDwRV6EK/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://buildpacks.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://buildpacks.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cZ1vU8/hyI2zcf2K2/gKgT0is9HxQVfRFoZKBnTK/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/fYgt5/hyI2FDxPha/wKGF9CHNNkjUfqcDwRV6EK/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;&gt;Cloud Native Buildpacks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Cloud Native Buildpacks transform your application source code into images that can run on any cloud.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;buildpacks.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;백문이불여일견 기존에 개발하고 있던 프로젝트(2.2.6)를 한번 2.3.8로 버전을 올리고, 내장된 BuildPack 라이브러리를 통해 Docker Image를 만들어 보겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;[주의사항]&lt;br /&gt;Spring 2.3 버전부턴 javax 패키지 하위에 있던 validation class들이 spring-boot-starter-validation이라는 스타터 라이브러리로 분리되어 배포된다. 2.3 버전 이하에선 spring-boot-starter-web 하위에 포함되어있었기에 2.2 버전을 사용하다가 2.3 버전으로 올릴 때 javax 패키지 하위에 있는 validation class를 사용한 코드가 있는 경우 에러가 난다.&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;script id=&quot;asciicast-386645&quot; src=&quot;https://asciinema.org/a/386645.js&quot; async&gt;&lt;/script&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;gradle wrapper를 통해 빌드를 실행하고, plain 모드로 각 task가 어떻게 진행되는지 봤다. bootBuildImage task를 실행하면, buildPack에서 애플리케이션을 도커 이미지로 만들기 위해 필요한 buildpack 도커 이미지를 먼저 pulling 한다.&lt;/p&gt;
&lt;p&gt;그 이후, 도커 이미지를 만들면서 실행된 각종 단계들이 로그로 찍히게 된다. 참고로 buildpack은 애플리케이션이 구동되기 위해 필요한 jvm 사이즈를 알아서 계산해서 넣어준다. 이는 컨테이너로 애플리케이션을 구동할 때 메모리를 최적으로 사용하도록 하기 위함인 것 같다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;다음은 직접 Dockerfile을 통해 도커 이미지로 만드는 방법이다. 가장 먼저 build.gradle에 다음과 같이 task를 추가하자.&lt;/p&gt;
&lt;pre id=&quot;code_1611491291462&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// separate jar into app and library layers
task moveLib {
    doLast {
        def unpackDir = &quot;$buildDir/unpack&quot;
        ant.move(file: &quot;${unpackDir}/app/BOOT-INF/lib&quot;, toFile: &quot;${unpackDir}/lib&quot;)
    }
}

task unpackJar(type: Copy) {
    def unpackDir = &quot;$buildDir/unpack&quot;

    delete unpackDir
    from zipTree(jar.getArchiveFile())
    into &quot;$unpackDir/app&quot;

    finalizedBy moveLib
}

build {
    finalizedBy unpackJar
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;fatJar 파일을 app과 lib디렉토리로 분리한다. 다음은 Dockerfile을 다음과 같이 작성해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1611491429985&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Build Stage
FROM gradle:6.8.0-jdk8 AS builder
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME

# Only copy dependency-related files
COPY build.gradle settings.gradle ./

# Copy Sourcefile
COPY src src

# build soruce file with dependnecies
RUN gradle clean build --console=plain -x test

# Runtime Stage
FROM openjdk:8-jre-alpine

ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
ARG buildDir=$APP_HOME/build/unpack

COPY --from=builder ${buildDir}/lib BOOT-INF/lib
COPY --from=builder ${buildDir}/app .

EXPOSE 8080
CMD [&quot;java&quot;, &quot;org.springframework.boot.loader.JarLauncher&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 다음 명령어를 통해 Dockerfile을 빌드하고 도커 이미지를 만들어보자. -f 옵션 뒤에 붙는 부분은 내 Dockerfile이 다른 디렉터리 하위에 있기 때문이다. Dockerfile의 위치를 설정하는 옵션으로 프로젝트 루트 경로에 Dockerfile이 바로 있다면 작성 안 해줘도 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1611491487928&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker buid -t user-test-dockerfile -f docker/Dockerfile .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;script id=&quot;asciicast-zDaMoz5OOBrZf2B3sawPeoe9d&quot; src=&quot;https://asciinema.org/a/zDaMoz5OOBrZf2B3sawPeoe9d.js&quot; async&gt;&lt;/script&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;도커 이미지가 생성된 사이즈를 보니, 기존에 build.gradlew에서 bootJar Task에서 layered 구문으로 fatJar를 분리하고, Dockerifle에서 build stage를 통해 빌드한 이미지 사이즈보다 크게 나온것을 확인할 수 있었다(ㅡ,ㅡ;;).&lt;/p&gt;
&lt;p&gt;Dockerfile은 각 명령어 라인들이 도커파일 이미지의 layer가 되고 용량을 잡아먹기 때문에 최소화하는 게 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;docker history 명령어를 통해 buildpack으로 생성된 이미지의 layer를 확인해보니 꽤 많이 생겨있는 것을 확인할 수 있는데, 이 점은 단점일 수도 장점일 수 도있다. 왜냐하면 도커는 기본적으로 도커 이미지를 만들 때 각 layer에 대한 캐싱 기능을 지원한다. 따라서 상세하게 나뉜 layer는 빌드 타임을 많이 줄일 수 있다는 장점이 있으면서, 도커 이미지의 크기가 커질 수도 있다는 단점이 존재한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 buildPack의 경우, 도커 이미지를 만들때 필요한 커스텀 기능들이 많이 부족하다, EXPOSE 또는 파일을 옮기기 위한 COPY의 기능, RUN 등 옵션들을 지정할 수 없는 것 같다. build image를 변경하면 해결이 되는지는 모르겠지만,, buildpack을 이용한 dockerizing은 아직 시기상조일 것 같다.&lt;/p&gt;</description>
      <category>Programming/Spring</category>
      <category>BuildPack</category>
      <category>docker</category>
      <category>Docker Image Optimization</category>
      <category>Dockerizing</category>
      <category>SpringBoot Docker Image</category>
      <category>SpringBoot Dockerizing</category>
      <category>도커</category>
      <category>도커 이미지</category>
      <category>도커라이징</category>
      <category>스프링부트 도커</category>
      <author>JohnMark</author>
      <guid isPermaLink="true">https://johnmarc.tistory.com/133</guid>
      <comments>https://johnmarc.tistory.com/133#entry133comment</comments>
      <pubDate>Sun, 24 Jan 2021 21:38:23 +0900</pubDate>
    </item>
  </channel>
</rss>