<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>wellbell의 개발일기</title>
    <link>https://wellbell.tistory.com/</link>
    <description>깃허브 주소 : https://github.com/whdals7337</description>
    <language>ko</language>
    <pubDate>Tue, 11 Aug 2026 08:49:51 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>wellbell</managingEditor>
    <item>
      <title>Resilience4j, Spring Boot 설정 공부</title>
      <link>https://wellbell.tistory.com/256</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Core modules&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resilience4j는 5개의 Core modules을 제공합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://resilience4j.readme.io/docs/circuitbreaker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;CircuitBreaker&lt;/b&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cgr7Z/btrfUOkCb9Y/ikcmaC8PzO4x1QRk8uwZvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cgr7Z/btrfUOkCb9Y/ikcmaC8PzO4x1QRk8uwZvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cgr7Z/btrfUOkCb9Y/ikcmaC8PzO4x1QRk8uwZvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCgr7Z%2FbtrfUOkCb9Y%2FikcmaC8PzO4x1QRk8uwZvK%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;452&quot; height=&quot;182&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;CircuitBreaker 패턴을 구현한 모듈입니다. 호출의 결과를 저장하고 집계(Aggregate)하기 위한 슬라이딩 윈도를 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;요청 실패율(Failure rate)이 몇 퍼센트 이상일 때 서킷의 상태가 CLOSED에서 OPEN으로 바뀔지 그리고 요청 중 지연된 응답(Slow calls)이 몇 퍼센트 이상일 때 서킷의 상태가 CLOSED에서 OPEN으로 바뀔지 설정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;슬라이딩 윈도우 크기가 15인 경우에도. 슬라이딩 윈도우가 15개의 요청만 동시에 실행할 수 있다는 것을 의미하지는 않습니다. 동시 스레드 수를 제한하려면 벌크헤드를 사용해야한다.(아래에서 다룸)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;두 가지 종류의 윈도우 중 하나를 선택하여 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 마지막 N개의 요청의 결과를 집계하는 카운트 베이스 윈도우(count-based window)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;- 설정한 window size N만큼의 값을 가질 수 있는 &lt;span style=&quot;color: #4f5a66;&quot;&gt;circular array로 구현&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #4f5a66;&quot;&gt;&amp;nbsp;- 새 요청 결과가 기록되면 업데이트되며 가장 오래된 측정값이 제거되면 측정값이 총집계에서 차감되고 버킷 재설정&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #4f5a66;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;스냅샷은 미리 집계되고 창 크기와 독립적이므로 스냅샷 검색 시간 복잡도는 O(1)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #4f5a66;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;공간 요구 사항(메모리 소비량)은 O(n)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;2. 마지막 N초 동안 요청의 결과를 집계하는 시간 베이스 윈도우(time-based window)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;N 부분 집합(버킷)의 &amp;nbsp;circular&amp;nbsp;array로 구현&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;모든 버킷은 특정 초(epoch second)에 발생하는 모든 요청의 결과를 집계&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;가장 오래된 버킷이 제거되면 해당 버킷의 총 집계가 총 집계에서 부분적으로 차감되고 버킷 재설정&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;스냅샷은 미리 집계되고 기간 크기와 독립적이므로 스냅샷 검색 시간 복잡도는 O(1)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&amp;nbsp;- &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;공간 요구 사항(메모리 소비)은 거의 일정한 O(n), N개의 부분집계와 1개의 총집계만 생성&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&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%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;propertyDefault&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ValueDescription&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failureRateThreshold&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;실패율(failure ratio) threshold퍼센트 값&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;실패율 임계값보다 크거나 같으면 CircuitBreaker가 개방으로 전환하고 단락 호출을 시작&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slowCallRateThreshold&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;지연된 응답(failure ratio) threshold 퍼센트 값&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;느린 호출의 백분율이 임계값과 같거나 크면 CircuitBreaker가 개방으로 전환되고 단락 호출이 시작&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slowCallDurationThreshold&lt;/td&gt;
&lt;td&gt;60000 [ms]&lt;/td&gt;
&lt;td&gt;요청이 느린 것으로 간주되는 기간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;permittedNumberOfCalls&lt;br /&gt;InHalfOpenState&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Half-open 상태에서 허가된 요청 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maxWaitDurationInHalfOpenState&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Half-open 상태에서 대기할 수 있는 최대 시간&lt;br /&gt;&lt;br /&gt;0은 무한정 기다리는 것을 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slidingWindowType&lt;/td&gt;
&lt;td&gt;COUNT_BASED&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;CircuitBreaker가 닫혔을 때 호출 결과를 기록하는 데 사용되는 슬라이딩 윈도우의 유형을 구성합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;COUNT_BASED이면 마지막 슬라이딩 WindowSize 호출이 기록되고 집계&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;TIME_BASSED이면 마지막 슬라이딩 WindowSize 초의 호출이 기록되고 집계&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slidingWindowSize&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;서킷의 상태가 CLOSED일 때 요청의 결과를 기록하기 위한 슬라이딩 윈도의 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;minimumNumberOfCalls&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;서킷이 실패율(failure rate) 또는 지연된 응답(slow call rate)을 계산하기 전 요구되는 최소 요청의 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;waitDurationInOpenState&lt;/td&gt;
&lt;td&gt;60000 [ms]&lt;/td&gt;
&lt;td&gt;서킷이 OPEN 에서 Half-open으로 변경되기 전 대기하는 시간 (이 시간 이후 변경된다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;automaticTransition&lt;br /&gt;FromOpenToHalfOpenEnabled&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;true 라면&lt;span&gt;&amp;nbsp;&lt;/span&gt;waitDurationInOpenState 기간이 지난 이후에 Open에서 Half-open으로 자동으로 상태가 변경된다. 하나의 쓰레드가 CircuitBreaker의 모든 인스턴스들을 모니터링하며 상태를 확인한다.&lt;br /&gt;&lt;br /&gt;false라면 요청이 있을 때만 상태가 Half-open으로 변경된다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;waitDurationInOpenState 기간이 지난 후에 새로운 요청이 있으면 Half-open 상태로 변경된다.모니터링을 위한 쓰레드가 필요없는 이점이 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;recordExceptions&lt;/td&gt;
&lt;td&gt;empty&lt;/td&gt;
&lt;td&gt;&lt;span&gt;실패로 기록될 예외의 리스트로 실패율(failure rate)가 증가되는 예외 리스트이다.&lt;br /&gt;예외 리스트를 설정한다면, 다른 모든 예외는 ignoreExceptions에 의해 무시되지 않는다면 성공으로 간주된다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ignoreExceptions&lt;/td&gt;
&lt;td&gt;empty&lt;/td&gt;
&lt;td&gt;&lt;span&gt;성공 또는 실패로 기록되지 않는 예외들의 리스트이다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;recordException&lt;/td&gt;
&lt;td&gt;throwable -&amp;gt; true&lt;br /&gt;&lt;br /&gt;By default all exceptions are recored as failures.&lt;/td&gt;
&lt;td&gt;커스텀 Predicate로 예외가 실패로 기록될지 정의 할 수 있다.&lt;br /&gt;예외가 실패로 카운트 되야 한다면 true를 리턴하고 성공으로 카운트 되야 한다면 false를 리턴해야 한다. (ignoreExceptions에 의해 무시되지 않는 경우)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ignoreException&lt;/td&gt;
&lt;td&gt;throwable -&amp;gt; false&lt;br /&gt;&lt;br /&gt;By default no exception is ignored.&lt;/td&gt;
&lt;td&gt;커스텀 Predicate로 예외가 무시될지 정의할 수 있다.&lt;br /&gt;예외가 무시되려면 true를 리턴하고 실패로 카운트 되려면 false를 리턴해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1632568001640&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코드 예시
// Create a custom configuration for a CircuitBreaker
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
  .failureRateThreshold(50)
  .slowCallRateThreshold(50)
  .waitDurationInOpenState(Duration.ofMillis(1000))
  .slowCallDurationThreshold(Duration.ofSeconds(2))
  .permittedNumberOfCallsInHalfOpenState(3)
  .minimumNumberOfCalls(10)
  .slidingWindowType(SlidingWindowType.TIME_BASED)
  .slidingWindowSize(5)
  .recordException(e -&amp;gt; INTERNAL_SERVER_ERROR
                 .equals(getResponse().getStatus()))
  .recordExceptions(IOException.class, TimeoutException.class)
  .ignoreExceptions(BusinessException.class, OtherBusinessException.class)
  .build();

// Create a CircuitBreakerRegistry with a custom global configuration
CircuitBreakerRegistry circuitBreakerRegistry = 
  CircuitBreakerRegistry.of(circuitBreakerConfig);

// Get or create a CircuitBreaker from the CircuitBreakerRegistry 
// with the global default configuration
CircuitBreaker circuitBreakerWithDefaultConfig = 
  circuitBreakerRegistry.circuitBreaker(&quot;name1&quot;);

// Get or create a CircuitBreaker from the CircuitBreakerRegistry 
// with a custom configuration
CircuitBreaker circuitBreakerWithCustomConfig = circuitBreakerRegistry
  .circuitBreaker(&quot;name2&quot;, circuitBreakerConfig);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632568020196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스프링부트 설정 예시
resilience4j.circuitbreaker:
    configs:
        default:
            registerHealthIndicator: true
            slidingWindowSize: 10
            minimumNumberOfCalls: 5
            permittedNumberOfCallsInHalfOpenState: 3
            automaticTransitionFromOpenToHalfOpenEnabled: true
            waitDurationInOpenState: 5s
            failureRateThreshold: 50
            eventConsumerBufferSize: 10
            recordExceptions:
                - org.springframework.web.client.HttpServerErrorException
                - java.util.concurrent.TimeoutException
                - java.io.IOException
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
        shared:
            slidingWindowSize: 100
            permittedNumberOfCallsInHalfOpenState: 30
            waitDurationInOpenState: 1s
            failureRateThreshold: 50
            eventConsumerBufferSize: 10
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
    instances:
        backendA:
            baseConfig: default&lt;/code&gt;&lt;/pre&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;a href=&quot;https://resilience4j.readme.io/docs/bulkhead&quot;&gt;&lt;b&gt;Bulkhead&lt;/b&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resilience4j는&amp;nbsp;동시 실행(Concurrent execution) 수를 제한하는데 사용되는&amp;nbsp;두개의 bulkhead 패턴 구현체를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;SemaphoreBulkhead는 다양한 스레딩 및 I/O 모델에서 잘 작동&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;벌크헤드 구성과 일치하는 올바른 스레드 풀 크기를 확인하는 것은 클라이언트에 달려 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Semaphores를 사용하는&amp;nbsp;SemaphoreBulkhead&lt;/li&gt;
&lt;li&gt;고정된 Thread pool과 Bounded queue를 사용하는&amp;nbsp;FixedThreadPoolBulkHead&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SemaphoreBulkhead Config&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&gt;&lt;b&gt;Config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;propertyDefault&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;valueDescription&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maxConcurrentCalls&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;Bulkhead에 의해 허가된 동시 실행 최대 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maxWaitDuration&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;포화 상태의 Bulkhead에 진입하기 위해 block 되는 최대 시간&lt;br /&gt;값이 0인 경우에는 바로 요청을 막는다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1632568182243&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코드 예시
// Create a custom configuration for a Bulkhead
BulkheadConfig config = BulkheadConfig.custom()
    .maxConcurrentCalls(150)
    .maxWaitDuration(Duration.ofMillis(500))
    .build();

// Create a BulkheadRegistry with a custom global configuration
BulkheadRegistry registry = BulkheadRegistry.of(config);

// Get or create a Bulkhead from the registry - 
// bulkhead will be backed by the default config
Bulkhead bulkheadWithDefaultConfig = registry.bulkhead(&quot;name1&quot;);

// Get or create a Bulkhead from the registry, 
// use a custom configuration when creating the bulkhead
Bulkhead bulkheadWithCustomConfig = registry.bulkhead(&quot;name2&quot;, custom);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632568241039&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스프링부트 설정 예시
resilience4j.bulkhead:
    configs:
        default:
            maxConcurrentCalls: 100
    instances:
        backendA:
            maxConcurrentCalls: 10
        backendB:
            maxWaitDuration: 10ms
            maxConcurrentCalls: 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;FixedThreadPoolBulkHead Config&lt;/b&gt;&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&gt;&lt;b&gt;Config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;propertyDefault&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;valueDescription&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maxThreadPoolSize&lt;/td&gt;
&lt;td&gt;Runtime.getRuntime()&lt;br /&gt;.availableProcessors()&lt;/td&gt;
&lt;td&gt;최대 쓰레드 풀 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;coreThreadPoolSize&lt;/td&gt;
&lt;td&gt;Runtime.getRuntime()&lt;br /&gt;.availableProcessors() - 1&lt;/td&gt;
&lt;td&gt;코어 쓰레드 풀 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;queueCapacity&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;큐의 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;keepAliveDuration&lt;/td&gt;
&lt;td&gt;20 [ms]&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스레드 수가 코어보다 크면 초과된 유휴 스레드가 종료하기 전에 새 작업을 대기하는 최대 시간&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1632568308781&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코드 예시
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
  .maxThreadPoolSize(10)
  .coreThreadPoolSize(2)
  .queueCapacity(20)
  .build();
        
// Create a BulkheadRegistry with a custom global configuration
ThreadPoolBulkheadRegistry registry = ThreadPoolBulkheadRegistry.of(config);

// Get or create a ThreadPoolBulkhead from the registry - 
// bulkhead will be backed by the default config
ThreadPoolBulkhead bulkheadWithDefaultConfig = registry.bulkhead(&quot;name1&quot;);

// Get or create a Bulkhead from the registry, 
// use a custom configuration when creating the bulkhead
ThreadPoolBulkheadConfig custom = BulkheadConfig.custom()
  .maxThreadPoolSize(5)
  .build();

ThreadPoolBulkhead bulkheadWithCustomConfig = registry.bulkhead(&quot;name2&quot;, custom);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632568264626&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스프링부트 설정 예시
resilience4j.thread-pool-bulkhead:
    configs:
        default:
            maxThreadPoolSize: 4
            coreThreadPoolSize: 2
            queueCapacity: 2
    instances:
        backendA:
            baseConfig: default
        backendB:
            maxThreadPoolSize: 1
            coreThreadPoolSize: 1
            queueCapacity: 1&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://resilience4j.readme.io/docs/ratelimiter&quot;&gt;RateLimiter&lt;/a&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJMuqB/btrfU32Xuax/Knlvn2HNadYASnW94iQJV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJMuqB/btrfU32Xuax/Knlvn2HNadYASnW94iQJV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJMuqB/btrfU32Xuax/Knlvn2HNadYASnW94iQJV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJMuqB%2FbtrfU32Xuax%2FKnlvn2HNadYASnW94iQJV1%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;716&quot; height=&quot;380&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;일정 시간동안 요청 수를 제한하는데 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;속도 제한은 API의 확장에 대비하고 서비스의 높은 가용성과 안정성을 확립하기 위해 반드시 필요한 기술&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;제한 초과 요청을 거부하거나 대기열을 작성하여 나중에 실행하거나 이 두 가지 접근 방식을 조합할 수 있습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;/span&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&gt;&lt;b&gt;Config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;propertyDefault&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;valueDescription&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;timeoutDuration&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;5 [s]&lt;/td&gt;
&lt;td&gt;허가(Permission)을 위해 쓰레드가 대기하는 기본 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;limitRefreshPeriod&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;500 [ns]&lt;/td&gt;
&lt;td&gt;Limit refresh 기간으로, 각 기간 이후에 RateLimiter가 일정 시간 동안 허가되는 요청 수를 다시 설정한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;limitForPeriod&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;한 Limit refresh 기간 동안 허가되는 요청 수&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1632568807373&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코드 예시
RateLimiterConfig config = RateLimiterConfig.custom()
  .limitRefreshPeriod(Duration.ofMillis(1))
  .limitForPeriod(10)
  .timeoutDuration(Duration.ofMillis(25))
  .build();

// Create registry
RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config);

// Use registry
RateLimiter rateLimiterWithDefaultConfig = rateLimiterRegistry
  .rateLimiter(&quot;name1&quot;);

RateLimiter rateLimiterWithCustomConfig = rateLimiterRegistry
  .rateLimiter(&quot;name2&quot;, config);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632568836345&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스프링부트 설정 예시
resilience4j.ratelimiter:
    configs:
        default:
            registerHealthIndicator: false
            limitForPeriod: 10
            limitRefreshPeriod: 1s
            timeoutDuration: 0
            eventConsumerBufferSize: 100
    instances:
        backendA:
            baseConfig: default
        backendB:
            limitForPeriod: 6
            limitRefreshPeriod: 500ms
            timeoutDuration: 3s&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://resilience4j.readme.io/docs/retry&quot;&gt;Retry&lt;/a&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;요청이 실패했을 경우 재시도 정책에 관련한 조건을 관리하기 위해 사용됩니다.&lt;/span&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&gt;&lt;b&gt;Config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;propertyDefault&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;valueDescription&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;maxAttempts&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;최대 재시도 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;waitDuration&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;500 [ms]&lt;/td&gt;
&lt;td&gt;재시도 사이에 고정된 대기 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;intervalFunction&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;numOfAttempts -&amp;gt; waitDuration&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;요청 실패 이후 대기 간격을 수정하기 위한 함수이다. 기본적으로는 대기 시간(waitDuration)이 일정하게 유지된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;retryOnResultPredicate&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;result -&amp;gt; false&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;결과에 따라 재시도 여부를 결정하기 위한 Predicate를 설정한다.&lt;br /&gt;만약 결과가 재시도 되야 한다면, true를 리턴해야하고 그렇지 않다면 false를 리턴해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;retryOnExceptionPredicate&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;throwable -&amp;gt; true&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;예외(Exception)에 따라 재시도 여부를를 결정하기 위한 Predicate를 설정한다.&lt;br /&gt;만약 예외에 따라 재시도 되야 한다면, true를 리턴해야하고 그렇지 않다면 false를 리턴해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;retryExceptions&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;empty&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;실패로 기록되는 에러 클래스 리스트 즉, 재시도 되야하는 에러 클래스의 리스트이다.&lt;br /&gt;&lt;br /&gt;empty일 경우 모든 에러 클래스를 재시도 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;ignoreExceptions&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;empty&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;무시되야 하는 에러 클래스 리스트 즉, 재시도 되지 않아야 할 에러 클래스 리스트이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1632568961160&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코드 예시
RetryConfig config = RetryConfig.custom()
  .maxAttempts(2)
  .waitDuration(Duration.ofMillis(1000))
  .retryOnResult(response -&amp;gt; response.getStatus() == 500)
  .retryOnException(e -&amp;gt; e instanceof WebServiceException)
  .retryExceptions(IOException.class, TimeoutException.class)
  .ignoreExceptions(BusinessException.class, OtherBusinessException.class)
  .failAfterMaxAttempts(true)
  .build();

// Create a RetryRegistry with a custom global configuration
RetryRegistry registry = RetryRegistry.of(config);

// Get or create a Retry from the registry - 
// Retry will be backed by the default config
Retry retryWithDefaultConfig = registry.retry(&quot;name1&quot;);

// Get or create a Retry from the registry, 
// use a custom configuration when creating the retry
RetryConfig custom = RetryConfig.custom()
    .waitDuration(Duration.ofMillis(100))
    .build();

Retry retryWithCustomConfig = registry.retry(&quot;name2&quot;, custom);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632568981603&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스프링부트 설정 예시
resilience4j.retry:
    configs:
        default:
            maxAttempts: 3
            waitDuration: 100
            retryExceptions:
                - org.springframework.web.client.HttpServerErrorException
                - java.util.concurrent.TimeoutException
                - java.io.IOException
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
    instances:
        backendA:
            baseConfig: default
        backendB:
            baseConfig: default&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://resilience4j.readme.io/docs/timeout&quot;&gt;TimeLimiter&lt;/a&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;원격 서버를 호출하는데 걸리는 시간을 제한할 수 있습니다.&lt;/span&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&gt;&lt;b&gt;Config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;propertyDefault&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;valueDescription&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;timeoutDuration&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;1 [s]&lt;/td&gt;
&lt;td&gt;Timeout 값 (기본 단위는 ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;cancelRunningFuture&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;Timeout 발생 후 future를 취소할지 결정하는 Boolean 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1632569044612&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코드 예시
TimeLimiterConfig config = TimeLimiterConfig.custom()
   .cancelRunningFuture(true)
   .timeoutDuration(Duration.ofMillis(500))
   .build();

// Create a TimeLimiterRegistry with a custom global configuration
TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.of(config);

// Get or create a TimeLimiter from the registry - 
// TimeLimiter will be backed by the default config
TimeLimiter timeLimiterWithDefaultConfig = registry.timeLimiter(&quot;name1&quot;);

// Get or create a TimeLimiter from the registry, 
// use a custom configuration when creating the TimeLimiter
TimeLimiterConfig config = TimeLimiterConfig.custom()
   .cancelRunningFuture(false)
   .timeoutDuration(Duration.ofMillis(1000))
   .build();

TimeLimiter timeLimiterWithCustomConfig = registry.timeLimiter(&quot;name2&quot;, config);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632569062012&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스프링부트 설정 예시
resilience4j.timelimiter:
    configs:
        default:
            cancelRunningFuture: false
            timeoutDuration: 2s
    instances:
        backendA:
            baseConfig: default
        backendB:
            baseConfig: default&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/resilience4j/resilience4j-spring-boot2-demo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/resilience4j/resilience4j-spring-boot2-demo&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://resilience4j.readme.io/docs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://resilience4j.readme.io/docs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leejongchan.tistory.com/100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leejongchan.tistory.com/100&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/256</guid>
      <comments>https://wellbell.tistory.com/256#entry256comment</comments>
      <pubDate>Sat, 25 Sep 2021 20:27:27 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 거리두기 확인하기 문제(자바)</title>
      <link>https://wellbell.tistory.com/254</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/81302&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1631023616941&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;[[&amp;quot;POOOP&amp;quot;, &amp;quot;OXXOX&amp;quot;, &amp;quot;OPXPX&amp;quot;, &amp;quot;OOXOX&amp;quot;, &amp;quot;POXXP&amp;quot;], [&amp;quot;POOPX&amp;quot;, &amp;quot;OXPXP&amp;quot;, &amp;quot;PXXXO&amp;quot;, &amp;quot;OXXXO&amp;quot;, &amp;quot;OOOPP&amp;quot;], [&amp;quot;PXOPX&amp;quot;, &amp;quot;OXOXP&amp;quot;, &amp;quot;OXPOX&amp;quot;, &amp;quot;OXXOP&amp;quot;, &amp;quot;PXPOX&amp;quot;], [&amp;quot;OOOXX&amp;quot;, &amp;quot;XOOOX&amp;quot;, &amp;quot;OOOXX&amp;quot;, &amp;quot;OXOOX&amp;quot;, &amp;quot;OOOOO&amp;quot;], [&amp;quot;PXPXP&amp;quot;, &amp;quot;XPXPX&amp;quot;, &amp;quot;PXPXP&amp;quot;, &amp;quot;XPXPX&amp;quot;, &amp;quot;PXPXP&amp;quot;]] [1, 0, 1, 1, 1]&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/djfrBV/hyLxuzAnJ1/7PZNkLKG9nFxpWRTykriL0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/FUrev/hyLxHr9YHn/DaqyKAV4Dutp7OEKyErmg0/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/81302&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/djfrBV/hyLxuzAnJ1/7PZNkLKG9nFxpWRTykriL0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/FUrev/hyLxHr9YHn/DaqyKAV4Dutp7OEKyErmg0/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;[[&quot;POOOP&quot;, &quot;OXXOX&quot;, &quot;OPXPX&quot;, &quot;OOXOX&quot;, &quot;POXXP&quot;], [&quot;POOPX&quot;, &quot;OXPXP&quot;, &quot;PXXXO&quot;, &quot;OXXXO&quot;, &quot;OOOPP&quot;], [&quot;PXOPX&quot;, &quot;OXOXP&quot;, &quot;OXPOX&quot;, &quot;OXXOP&quot;, &quot;PXPOX&quot;], [&quot;OOOXX&quot;, &quot;XOOOX&quot;, &quot;OOOXX&quot;, &quot;OXOOX&quot;, &quot;OOOOO&quot;], [&quot;PXPXP&quot;, &quot;XPXPX&quot;, &quot;PXPXP&quot;, &quot;XPXPX&quot;, &quot;PXPXP&quot;]] [1, 0, 1, 1, 1]&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;BFS를 이용하여 풀면되는 문제로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람 위치의 기준 상하좌우의 맨해튼 거리는 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;293&quot; data-origin-height=&quot;130&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/do0YvO/btreotay7Ry/KoZzoBGV9kGOHLw6wW7Gyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/do0YvO/btreotay7Ry/KoZzoBGV9kGOHLw6wW7Gyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/do0YvO/btreotay7Ry/KoZzoBGV9kGOHLw6wW7Gyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdo0YvO%2Fbtreotay7Ry%2FKoZzoBGV9kGOHLw6wW7Gyk%2Fimg.png&quot; data-origin-width=&quot;293&quot; data-origin-height=&quot;130&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;사람으로 부터 맨해튼 거리 1인 지점의 상하좌우의 맨해튼 거리는 2&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;133&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqzNDq/btrehG9CJH5/eyz9eD7WMbA4V5srRQ9JK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqzNDq/btrehG9CJH5/eyz9eD7WMbA4V5srRQ9JK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqzNDq/btrehG9CJH5/eyz9eD7WMbA4V5srRQ9JK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqzNDq%2FbtrehG9CJH5%2Feyz9eD7WMbA4V5srRQ9JK1%2Fimg.png&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;133&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;사람 위치 부터 BFS를 시작해서 2번 진행하면 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무조건 2번 진행하는 것이 아닌 빈 테이블일 경우만 진행해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 아래와 같은 상황은 맨해튼 거리가 2 이지만 규칙을 지킨것이기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;204&quot; data-origin-height=&quot;70&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXXIZI/btreoTNBqXM/g5jPiS6si9DHiC9TRkPmZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXXIZI/btreoTNBqXM/g5jPiS6si9DHiC9TRkPmZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXXIZI/btreoTNBqXM/g5jPiS6si9DHiC9TRkPmZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXXIZI%2FbtreoTNBqXM%2Fg5jPiS6si9DHiC9TRkPmZK%2Fimg.png&quot; data-origin-width=&quot;204&quot; data-origin-height=&quot;70&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;마지막으로 대기실이 5개 이므로 방문처리, 대기실 등을 잘 초기화 해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1631023566054&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

// 프로그래머스 - 거리두기 확인하기 문제
class Site {
    private final int x;
    private final int y;
    private final int distance;

    Site(int x, int y, int distance) {
        this.x = x;
        this.y = y;
        this.distance = distance;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getDistance() {
        return distance;
    }
}

class Solution {
    // 상하좌우
    private static final int[] dx = {-1, 1, 0, 0};
    private static final int[] dy = {0, 0, 1, -1};
    // 대기실 크기
    private static final int ROW = 5;
    private static final int COLUMN = 5;
    // 대기실 내부 유형(사람, 빈 테이블)
    private static final char PERSON = 'P';
    private static final char EMPTY = 'O';

    public int[] solution(String[][] places) {
        int[] answer = new int[places.length];
        Arrays.fill(answer, 1);

        // 대기실 순회
        for (int x = 0; x &amp;lt; places.length; x++) {
            List&amp;lt;Site&amp;gt; people = new ArrayList&amp;lt;&amp;gt;();
            char[][] map = settingMapAndPeople(places[x], people);

            for (Site person : people) {
                if(!isBreakRule(person, map)) {
                    answer[x] = 0;
                    break;
                }
            }
        }
        return answer;
    }

    private char[][] settingMapAndPeople(String[] places, List&amp;lt;Site&amp;gt; people) {
        char[][] temp = new char[ROW][COLUMN];
        for (int i = 0; i &amp;lt; ROW; i++) {
            for(int j = 0 ; j &amp;lt; COLUMN; j++) {
                temp[i][j] = places[i].charAt(j);
                if (temp[i][j] == PERSON) {
                    people.add(new Site(i, j,0));
                }
            }
        }
        return temp;
    }

    private boolean isBreakRule(Site site, char[][] map) {
        Queue&amp;lt;Site&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        boolean[][] visited = new boolean[ROW][COLUMN];
        q.offer(site);

        while (!q.isEmpty()) {
            Site now = q.poll();

            // 현재 맨해튼 거리가 2 이상인 경우
            if (now.getDistance() &amp;gt;= 2) continue;

            visited[now.getX()][now.getY()] = true;

            for(int i = 0; i &amp;lt; 4; i++) {
                int tx = now.getX() + dx[i];
                int ty = now.getY() + dy[i];
                int tDistance =  now.getDistance() + 1;

                // 범위 밖 또는 이미 방문
                if (isOutRanged(tx, ty) || visited[tx][ty]) continue;

                // 사람인 경우
                if(map[tx][ty] == PERSON) {
                    return false;
                }

                // 빈 테이블
                if(map[tx][ty] == EMPTY) {
                    q.offer(new Site(tx, ty, tDistance));
                }
            }
        }

        return true;
    }

    private boolean isOutRanged(int x, int y) {
        return x &amp;lt; 0 || x &amp;gt;= ROW || y &amp;lt; 0 || y &amp;gt;= COLUMN;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/254</guid>
      <comments>https://wellbell.tistory.com/254#entry254comment</comments>
      <pubDate>Tue, 7 Sep 2021 23:16:40 +0900</pubDate>
    </item>
    <item>
      <title>스프링 멀티모듈 나누기 개념 및 실습(메이븐, 그래들)</title>
      <link>https://wellbell.tistory.com/253</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 웹툰 인턴십 과제를 진행하다가 서비스가 커지면서 기존 프로젝트를 멀티모듈로 나눠서 개발하게 되었는데&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;a href=&quot;https://github.com/whdals7337/maven-multi-module&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/whdals7337/maven-multi-module&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래들 프로젝트 코드 주소 : &lt;a href=&quot;https://github.com/whdals7337/gradle-multi-module&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/whdals7337/gradle-multi-module&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멀티모듈이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;멀티 모듈이란 개념은 한 프로젝트 안에 라이브러리처럼 사용 가능한 상태로 모듈들을 구성하고 그것들을 다른 모듈에서 가져다 쓸 수 있도록 만드는 것이라고 이해할 수 있습니다.&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;96&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2udh7/btrd9k6D5W1/5vud37kt2csqq5zgOeCfO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2udh7/btrd9k6D5W1/5vud37kt2csqq5zgOeCfO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2udh7/btrd9k6D5W1/5vud37kt2csqq5zgOeCfO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2udh7%2Fbtrd9k6D5W1%2F5vud37kt2csqq5zgOeCfO1%2Fimg.png&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;96&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;대부분의 서비스는 단일 프로젝트로 구성되는 일이 거의 없습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;아무리 작게 구성해도 일정 수준 이상의 트래픽을 감당하려면 사용자와의 접점을 담당하는 서버 DB와의 접점을 담당하는 서버로 구분하여 구성하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;이럴 경우 고민이 되는 것이 그럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;web과 api 모두에서 사용되는 클래스들은 어떻게 다룰 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 복사 &amp;amp; 붙여넣기&lt;/b&gt; - 실수의 여지, 한쪽이 변경되면 다른쪽도 변경해줘야하는 중복 관리 포인트가 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 공통으로 사용하는 모듈을 jar로 만들고 이를 메이븐 원격 저장소에 deploy, 사용하는 모듈에서 디펜던시에 추가하여 사용 - &lt;/b&gt;&lt;span style=&quot;color: #161209;&quot;&gt;공통으로 사용하는 모듈이 변경될때마다 버전 변경, &lt;span style=&quot;color: #161209;&quot;&gt;메이븐 원격 저장소에 deploy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 멀티모듈&lt;/b&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;인턴 당시 프로젝트가 커다란 프로젝트는 아니지만 역할(core, api, admin,consumer 등)이 분리되어야 된다고 생각하여 멀티모듈을 진행하였습니다.&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;(공통 DB 설정, api와 다른 모듈의 글로벌 예외처리 등 각 모듈마다 가지고 있어야하는 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kafka 등 특정 모듈에서만 사용하는 설정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련해서 우형 블로그에 좋은 글이 있어서 한번 읽어보시길 추천드립니다. &lt;a href=&quot;https://techblog.woowahan.com/2637/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://techblog.woowahan.com/2637/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;메이븐&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 간단한 프로젝트 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2YsmU/btreh2DBY8m/twJnZ8Kbus0xpFkbjei1y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2YsmU/btreh2DBY8m/twJnZ8Kbus0xpFkbjei1y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2YsmU/btreh2DBY8m/twJnZ8Kbus0xpFkbjei1y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2YsmU%2Fbtreh2DBY8m%2FtwJnZ8Kbus0xpFkbjei1y0%2Fimg.png&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;848&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;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;b&gt;저는 CORE 모듈을 만들어 주겠습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;264&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDsDUm/btreiauIveZ/DalUKv5kXJbwpOQ1mSoCh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDsDUm/btreiauIveZ/DalUKv5kXJbwpOQ1mSoCh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDsDUm/btreiauIveZ/DalUKv5kXJbwpOQ1mSoCh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDsDUm%2FbtreiauIveZ%2FDalUKv5kXJbwpOQ1mSoCh1%2Fimg.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;264&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;666&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMBowT/btreiPKM4Gw/QU5u2eXdNSwbQNsaqYXMFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMBowT/btreiPKM4Gw/QU5u2eXdNSwbQNsaqYXMFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMBowT/btreiPKM4Gw/QU5u2eXdNSwbQNsaqYXMFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMBowT%2FbtreiPKM4Gw%2FQU5u2eXdNSwbQNsaqYXMFk%2Fimg.png&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;666&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;만들고 나면 multi의 pom.xml이 아래와 같을 텐대&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;341&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6fWid/btrejXaJ2kA/kezObv3s27U77uXUVZzSkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6fWid/btrejXaJ2kA/kezObv3s27U77uXUVZzSkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6fWid/btrejXaJ2kA/kezObv3s27U77uXUVZzSkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6fWid%2FbtrejXaJ2kA%2FkezObv3s27U77uXUVZzSkk%2Fimg.png&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;341&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;packaging: pom, jar, war 등이 가능 한데 root의 경우 메이븐 package 될 때 자기 스스로 jar, war가 되는 것이 아닌 설정으로 셋팅 되어야 하기 때문에 pom으로 설정해줍니다. 차후 하위 모듈에서 package 진행시 multi의 설정이 합쳐지게 됩니다.&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;modules: 해당 프로젝트 하위에 있는 모듈로 차후 api 를 추가하고 보면 추가되어 있는것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parent: 스프링부트 프로젝트로 스프링 프레임워크 부트를 부모로 셋팅해둡니다. multi를 부모로 두는 core의 경우도 스프링 프레임워크 부트 관련하여 사용할 수 있습니다. (스프링 프레임워크 부트 - multi - core 순)&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;groupid, artifact, version 의 경우 아래와 같이 하위 모듈에서 부모를 설정할 때 사용하게 됩니다.&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;572&quot; data-origin-height=&quot;457&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHkbCk/btreiQv9jH4/iPDcxRxYLsREkl4aJ1p7Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHkbCk/btreiQv9jH4/iPDcxRxYLsREkl4aJ1p7Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHkbCk/btreiQv9jH4/iPDcxRxYLsREkl4aJ1p7Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHkbCk%2FbtreiQv9jH4%2FiPDcxRxYLsREkl4aJ1p7Y1%2Fimg.png&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;457&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;왼쪽 메이븐 탭의 의존성 부분을 보면 아래와 같이 multi에 설정한 의존성이&amp;nbsp; core에도 들어와 있는 것을 확인할 수 있는데 root의 pom의 설정의 하위 모듈 모두에서 사용된다고 보면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;380&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PZ66A/btrd66ugJMs/Gt1SoVYHbuXAO9MXveo3h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PZ66A/btrd66ugJMs/Gt1SoVYHbuXAO9MXveo3h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PZ66A/btrd66ugJMs/Gt1SoVYHbuXAO9MXveo3h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPZ66A%2Fbtrd66ugJMs%2FGt1SoVYHbuXAO9MXveo3h1%2Fimg.png&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;380&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;api모듈을 같은 방식으로 추가해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 src는 필요없으니 지우고 api 모듈에 ApiApplication 클래스(스프링 부트 메인)을 추가해준다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;323&quot; data-origin-height=&quot;580&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xMkmn/btrejfigBY9/8H9hAzRUWd3LxXQwbLcMu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xMkmn/btrejfigBY9/8H9hAzRUWd3LxXQwbLcMu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xMkmn/btrejfigBY9/8H9hAzRUWd3LxXQwbLcMu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxMkmn%2FbtrejfigBY9%2F8H9hAzRUWd3LxXQwbLcMu0%2Fimg.png&quot; data-origin-width=&quot;323&quot; data-origin-height=&quot;580&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;181&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PTpik/btrd64QMeKV/8gkoQk5g8S3PbTsaEip7P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PTpik/btrd64QMeKV/8gkoQk5g8S3PbTsaEip7P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PTpik/btrd64QMeKV/8gkoQk5g8S3PbTsaEip7P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPTpik%2Fbtrd64QMeKV%2F8gkoQk5g8S3PbTsaEip7P1%2Fimg.png&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;181&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. core에 공통 클래스 만들고 api에서 사용하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;core 하위에 Member라는 클래스를 만들어 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;414&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD2PxQ/btrd66nv3SS/E63jhYoXbPj3khQqQNMbB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD2PxQ/btrd66nv3SS/E63jhYoXbPj3khQqQNMbB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD2PxQ/btrd66nv3SS/E63jhYoXbPj3khQqQNMbB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD2PxQ%2Fbtrd66nv3SS%2FE63jhYoXbPj3khQqQNMbB0%2Fimg.png&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;414&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;api 모듈에서 해당 객체를 사용할려면 core 모듈 의존성을 아래 처럼 추가해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;649&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TISRC/btreh2KoGpV/Sgl04kAyifieR5ciy0x2Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TISRC/btreh2KoGpV/Sgl04kAyifieR5ciy0x2Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TISRC/btreh2KoGpV/Sgl04kAyifieR5ciy0x2Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTISRC%2Fbtreh2KoGpV%2FSgl04kAyifieR5ciy0x2Lk%2Fimg.png&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;649&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;추가해주면 아래와 같이 api 모듈에서 core의 객체를 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;330&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/df8czo/btrehBGhZeG/DN2jahpGtJIsHTKDii5QLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/df8czo/btrehBGhZeG/DN2jahpGtJIsHTKDii5QLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/df8czo/btrehBGhZeG/DN2jahpGtJIsHTKDii5QLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdf8czo%2FbtrehBGhZeG%2FDN2jahpGtJIsHTKDii5QLk%2Fimg.png&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;330&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;api 모듈의 ApiApplication을 통해서 실행 시키면 아래처럼 잘 나오는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;99&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnMWX1/btrd9kZ8CRv/40CNBn6Wl7fnGk8iAEomu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnMWX1/btrd9kZ8CRv/40CNBn6Wl7fnGk8iAEomu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnMWX1/btrd9kZ8CRv/40CNBn6Wl7fnGk8iAEomu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnMWX1%2Fbtrd9kZ8CRv%2F40CNBn6Wl7fnGk8iAEomu0%2Fimg.png&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;99&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. api package 진행&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1630933100284&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mvn package -pl api -am&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메이븐 명령어로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pl : package 대상 모듈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;am: 연결된 모듈을 같이 package 진행&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;실행해보면 아래처럼 되는데 이유는 core에는 스프링부트 메인 클래스가 없어서 인데 core는 혼자서 돌아가는 모듈이 아닌데 메인 클래스를 추가해야할까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;266&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC1FTs/btreiQbUiC8/KTvg7KeGXmMqZZe726EAoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC1FTs/btreiQbUiC8/KTvg7KeGXmMqZZe726EAoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC1FTs/btreiQbUiC8/KTvg7KeGXmMqZZe726EAoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC1FTs%2FbtreiQbUiC8%2FKTvg7KeGXmMqZZe726EAoK%2Fimg.png&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;266&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;답은 아니다. 아래는 multi의 pom으로 빌드 설정이 기본적으로 들어가 있는 것을 확인 할 수 있는데 이 설정은 하위 모듈 전체에 들어가게되고 해당 빌드를 따라서 core를 빌드하려고 하기 때문에 문제가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JtmIZ/btree4bbAI8/dgMQUWI3UJKLOTOQuSydZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JtmIZ/btree4bbAI8/dgMQUWI3UJKLOTOQuSydZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JtmIZ/btree4bbAI8/dgMQUWI3UJKLOTOQuSydZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJtmIZ%2Fbtree4bbAI8%2FdgMQUWI3UJKLOTOQuSydZk%2Fimg.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;684&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;이를 해결하는 방법으로 빌드 관련된 코드를 api쪽으로 이동 시키면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;707&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xAVOs/btrehHsOmdK/sAB5KLKBgKt4qkTKLqfzk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xAVOs/btrehHsOmdK/sAB5KLKBgKt4qkTKLqfzk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xAVOs/btrehHsOmdK/sAB5KLKBgKt4qkTKLqfzk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxAVOs%2FbtrehHsOmdK%2FsAB5KLKBgKt4qkTKLqfzk1%2Fimg.png&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;707&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;110&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7iQ40/btree5VpSdg/ovCKG2a4OIdU80BOHPaZj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7iQ40/btree5VpSdg/ovCKG2a4OIdU80BOHPaZj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7iQ40/btree5VpSdg/ovCKG2a4OIdU80BOHPaZj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7iQ40%2Fbtree5VpSdg%2FovCKG2a4OIdU80BOHPaZj0%2Fimg.png&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;110&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 따라오셨다면 발생하지 않겠지만 혹시 아래와 같은 문제가 발생한다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 설정이 core에 들어가 있지 않는지 확인하시고 없는데도 그렇다면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;214&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8SDX1/btrec5Bm1Eo/5m4ENCGvWsPK7px3vkdzX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8SDX1/btrec5Bm1Eo/5m4ENCGvWsPK7px3vkdzX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8SDX1/btrec5Bm1Eo/5m4ENCGvWsPK7px3vkdzX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8SDX1%2Fbtrec5Bm1Eo%2F5m4ENCGvWsPK7px3vkdzX0%2Fimg.png&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;214&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;core와 api의 base dir을 동일하게 맞추고 스캔 범위를 설정해 주면 해결 될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;274&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSJUxS/btreh3igjug/KIktKCuDR0FcPamK4QXkXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSJUxS/btreh3igjug/KIktKCuDR0FcPamK4QXkXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSJUxS/btreh3igjug/KIktKCuDR0FcPamK4QXkXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSJUxS%2Fbtreh3igjug%2FKIktKCuDR0FcPamK4QXkXK%2Fimg.png&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;274&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. jar 확인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api 모듈 하위 target에 아래처럼 jar 파일이 만들어지는데 해당 jar를 실행하여 확인하면 멀티 모듈 기본적인 셋팅이 되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;946&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QuS5c/btrd9l5NIpi/nVHUoiUwoiUhumA7rPXqFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QuS5c/btrd9l5NIpi/nVHUoiUwoiUhumA7rPXqFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QuS5c/btrd9l5NIpi/nVHUoiUwoiUhumA7rPXqFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQuS5c%2Fbtrd9l5NIpi%2FnVHUoiUwoiUhumA7rPXqFk%2Fimg.png&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;946&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;246&quot; data-origin-height=&quot;71&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bugwZL/btrd21NayMh/QI6khtA3L5oHej2jngJmc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bugwZL/btrd21NayMh/QI6khtA3L5oHej2jngJmc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bugwZL/btrd21NayMh/QI6khtA3L5oHej2jngJmc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbugwZL%2Fbtrd21NayMh%2FQI6khtA3L5oHej2jngJmc1%2Fimg.png&quot; data-origin-width=&quot;246&quot; data-origin-height=&quot;71&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래들&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래들의 경우 위의 메이븐과 다른 지점에 대해서만 설명드리겠습니다 .ㅎ&lt;/b&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;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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;114&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chGhKt/btrd21sUZlX/1EM5EP3q05a7CgiYyPykJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chGhKt/btrd21sUZlX/1EM5EP3q05a7CgiYyPykJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chGhKt/btrd21sUZlX/1EM5EP3q05a7CgiYyPykJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchGhKt%2Fbtrd21sUZlX%2F1EM5EP3q05a7CgiYyPykJ1%2Fimg.png&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;114&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;위의 설정이 메이븐에서는 pom.xml의 modules 부분입니다.&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;b&gt;subprojects 부분이 하위 모듈에 적용될 내용으로 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;플러그인 적용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;group, version, java version 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저장소 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;롬복 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의존성 설정(여기에 적으면 모든 하위 모듈에 적용 됨)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;913&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqdMLg/btrec5uHuHa/Sv4OLVSjvY5aujhrAyk8O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqdMLg/btrec5uHuHa/Sv4OLVSjvY5aujhrAyk8O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqdMLg/btrec5uHuHa/Sv4OLVSjvY5aujhrAyk8O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeqdMLg%2Fbtrec5uHuHa%2FSv4OLVSjvY5aujhrAyk8O0%2Fimg.png&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;913&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;core 그래들 설정으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성(여기에서 설정하면 core에만 의존성이 추가됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bootjar 설정 (앞서 메이븐에서 core가 jar 파일로 package되는 부분이 기본값인 반면 그래들에서는 아래와 같이 설정해야함)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;330&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vlwbF/btrehkdzViH/TM36OIGQCgdCAc6jgcWA3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vlwbF/btrehkdzViH/TM36OIGQCgdCAc6jgcWA3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vlwbF/btrehkdzViH/TM36OIGQCgdCAc6jgcWA3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvlwbF%2FbtrehkdzViH%2FTM36OIGQCgdCAc6jgcWA3k%2Fimg.png&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;330&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;api 에서 core 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;112&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9DCYG/btrehCyuuQy/1zaiPrVncjZDRl5JkIeCIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9DCYG/btrehCyuuQy/1zaiPrVncjZDRl5JkIeCIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9DCYG/btrehCyuuQy/1zaiPrVncjZDRl5JkIeCIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9DCYG%2FbtrehCyuuQy%2F1zaiPrVncjZDRl5JkIeCIk%2Fimg.png&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;112&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. controller 설정 및 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Member는 메이븐에서 사용한 것과 동일한 것을 core모듈에 생성후 아래 처럼 controller를 작성하여 실행하면 정상 작동합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;350&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccwFku/btreaRpr7Kv/H3PEZuukAUXLVxXXB36KC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccwFku/btreaRpr7Kv/H3PEZuukAUXLVxXXB36KC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccwFku/btreaRpr7Kv/H3PEZuukAUXLVxXXB36KC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccwFku%2FbtreaRpr7Kv%2FH3PEZuukAUXLVxXXB36KC1%2Fimg.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;350&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;261&quot; data-origin-height=&quot;87&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baReaP/btrd65a6oVp/DXqNdyysmRdPpZ7pP1VO20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baReaP/btrd65a6oVp/DXqNdyysmRdPpZ7pP1VO20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baReaP/btrd65a6oVp/DXqNdyysmRdPpZ7pP1VO20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaReaP%2Fbtrd65a6oVp%2FDXqNdyysmRdPpZ7pP1VO20%2Fimg.png&quot; data-origin-width=&quot;261&quot; data-origin-height=&quot;87&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. jar 확인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래들에서는 bootjar를 진행하면 알아서 연결된 모듈을 가져오기 때문에 아래와 같은 명령어를 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1630936099717&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./gradlew api:bootJar&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;690&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zBIo1/btreibf8Z2t/SBWEPjrcSzxHylOmwkUUvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zBIo1/btreibf8Z2t/SBWEPjrcSzxHylOmwkUUvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zBIo1/btreibf8Z2t/SBWEPjrcSzxHylOmwkUUvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzBIo1%2Fbtreibf8Z2t%2FSBWEPjrcSzxHylOmwkUUvk%2Fimg.png&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;690&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;</description>
      <category>Spring</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/253</guid>
      <comments>https://wellbell.tistory.com/253#entry253comment</comments>
      <pubDate>Mon, 6 Sep 2021 17:28:08 +0900</pubDate>
    </item>
    <item>
      <title>상속(Inheritance)과 위임(Delegation) 방식</title>
      <link>https://wellbell.tistory.com/252</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;상속&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;201&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ARUwx/btrdBrMu2G1/PlhROFyhgT1fnOjnugdW20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ARUwx/btrdBrMu2G1/PlhROFyhgT1fnOjnugdW20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ARUwx/btrdBrMu2G1/PlhROFyhgT1fnOjnugdW20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FARUwx%2FbtrdBrMu2G1%2FPlhROFyhgT1fnOjnugdW20%2Fimg.png&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;201&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;- 두 클래스 사이의 관계가 'IS-A' 관계일 때 사용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위임&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;164&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnYADw/btrdCyq6Tjk/Ron4MnobN5GljPWcPYJYxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnYADw/btrdCyq6Tjk/Ron4MnobN5GljPWcPYJYxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnYADw/btrdCyq6Tjk/Ron4MnobN5GljPWcPYJYxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnYADw%2FbtrdCyq6Tjk%2FRon4MnobN5GljPWcPYJYxk%2Fimg.png&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;164&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;- 스프링 부트 프로젝트 개발을 진행해 보았다면 아래와 같은 형태를 많이 사용했을탠대 위와 같은 코드가 위임으로 Skill에 대한 처리는 SkillService에 위임하여 처리한다고 보면 된다.&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;상속의 경우 부모 클래스와 자식 클래스 사이에 강한 연관 관계가 생겨 부모 클래스 동작이 변경되면 자식 클래스의 동작도 모두 영향을 받게 된다. 이러한 관계는 'IS-A'의 관계의 경우 자연스러운 편이지만 이 외에는 불필요한 종속성이 생기게 된다. 또한 'IS-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;/p&gt;</description>
      <category>JAVA</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/252</guid>
      <comments>https://wellbell.tistory.com/252#entry252comment</comments>
      <pubDate>Tue, 31 Aug 2021 16:12:05 +0900</pubDate>
    </item>
    <item>
      <title>AWS 프리티어 만료에 따른 자기소개 페이지 서비스 종료</title>
      <link>https://wellbell.tistory.com/248</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;340&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZBG7N/btrbkg5YcX7/kVs3kv9kdI1kbN87pFkwsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZBG7N/btrbkg5YcX7/kVs3kv9kdI1kbN87pFkwsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZBG7N/btrbkg5YcX7/kVs3kv9kdI1kbN87pFkwsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZBG7N%2Fbtrbkg5YcX7%2FkVs3kv9kdI1kbN87pFkwsk%2Fimg.png&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;340&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;기존에 Route 53을 사용하면서 매달 600원 수준을 지불했는데 8월 이후로 프리티어가 만료되어서 사용하는 EC2, CloudFront ,S3 , RDS 등에 대한 비용 청구될게 뻔해서 8월 5일 부로 AWS 프리티어가 만료되어 사용하던 리소스, 인스턴스를 모두 삭제하였습니다.&lt;/p&gt;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/248</guid>
      <comments>https://wellbell.tistory.com/248#entry248comment</comments>
      <pubDate>Thu, 5 Aug 2021 02:01:12 +0900</pubDate>
    </item>
    <item>
      <title>git lfs란</title>
      <link>https://wellbell.tistory.com/247</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;326&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uM3L5/btq8Ts2lMCF/BxDJdlDxILT3bgUMCFddtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uM3L5/btq8Ts2lMCF/BxDJdlDxILT3bgUMCFddtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uM3L5/btq8Ts2lMCF/BxDJdlDxILT3bgUMCFddtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuM3L5%2Fbtq8Ts2lMCF%2FBxDJdlDxILT3bgUMCFddtk%2Fimg.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;326&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git 에서 크기가 큰 파일을 다루기 위해서 github에서 만든 오픈 소스&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;github에서는 5&lt;b&gt;0MB 이상의 파일을 올리려고 하면 경고&lt;/b&gt;를 띄우고, &lt;b&gt;100MB 이상의 파일은 업로드가 불가능&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서 lfs를 사용해야합니다.&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;lfs는 용량이 큰 파일을 외부 스토리지에 올리고 리포지토리에서는 포인터로 관리 합니다.&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;사용 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법은 맥북을 기준으로 설명하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Homebrew를 통한 git-lfs 설치&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1625499252881&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install git-lfs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 brew가 안 되시는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://brew.sh/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;179&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMgpV/btq8SByCN8V/tUmmxNnu8TFMp8UUGunbUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMgpV/btq8SByCN8V/tUmmxNnu8TFMp8UUGunbUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMgpV/btq8SByCN8V/tUmmxNnu8TFMp8UUGunbUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMgpV%2Fbtq8SByCN8V%2FtUmmxNnu8TFMp8UUGunbUK%2Fimg.png&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;179&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;사진으 코드 부분을 복사하셔서 Homebrew를 설치 해주세요.&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. 깃 리포지터리에 lfs를 적용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 리포지토리 아닌 곳에서 명령를 치면 오류남&lt;/p&gt;
&lt;pre id=&quot;code_1625499281732&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git lfs install&lt;/code&gt;&lt;/pre&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;span style=&quot;color: #222426;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일 내용을 원격 서버에 저장하는 동안 큰 파일을 Git 내부의 텍스트 포인터로 대체&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625499359844&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git lfs track &quot;관리하고자 하는 파일 명&quot;
# ex) git lfs track &quot;*.psd&quot;&lt;/code&gt;&lt;/pre&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;span style=&quot;color: #222426;&quot;&gt;관리중인 내용이 정의된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;.gitattributes&lt;span style=&quot;color: #222426;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1625499381772&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git add .gitattributes&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;이 뒤로는 git을 사용하여서 commit, push 등을 진행하면됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git-lfs 를 설치하지 않은 상태에서 git-lfs를 사용한 리포지토리를 clone하는 경우 관리되는 파일의 text pointers로 남아 있게 되어서 다른 작업을 더 해줘야 합니다. 되도록 it-lfs를 설치하고 clone하는 것을 추천드립니다. &lt;a href=&quot;https://github.com/git-lfs/git-lfs/issues/325%20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;관련 링크&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/git-lfs/git-lfs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/git-lfs/git-lfs&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>git &amp;amp; github</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/247</guid>
      <comments>https://wellbell.tistory.com/247#entry247comment</comments>
      <pubDate>Tue, 6 Jul 2021 01:40:03 +0900</pubDate>
    </item>
    <item>
      <title>16. SonarLint (코드분석도구) 적용하기</title>
      <link>https://wellbell.tistory.com/246</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SonarLint&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SonarLint&lt;/b&gt;&lt;span style=&quot;color: #596172;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;코드를 작성할 때 품질 문제를 감지하고 수정하는 데 도움이되는 IDE 확장입니다. 맞춤법 검사기처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;SonarLint&lt;/b&gt;&lt;span style=&quot;color: #596172;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;코드를 커밋하기 전에 수정할 수 있도록 결함을 표시합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이 플러그인에서 SonarLint를 다운 받아주고 인텔리제이 restart를 하면 적용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;690&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx8HRd/btq8KR8kp5N/wvrGyN419qyEPwv77E4kCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx8HRd/btq8KR8kp5N/wvrGyN419qyEPwv77E4kCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx8HRd/btq8KR8kp5N/wvrGyN419qyEPwv77E4kCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx8HRd%2Fbtq8KR8kp5N%2FwvrGyN419qyEPwv77E4kCk%2Fimg.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;690&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이 하단에 보면 다음과 같은 탭을 확인하실 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;55&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3IFq8/btq8JjxuilN/PjvrU8AZLdSLQwEGPRHdx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3IFq8/btq8JjxuilN/PjvrU8AZLdSLQwEGPRHdx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3IFq8/btq8JjxuilN/PjvrU8AZLdSLQwEGPRHdx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3IFq8%2Fbtq8JjxuilN%2FPjvrU8AZLdSLQwEGPRHdx1%2Fimg.png&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;55&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 간단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 파일을 열고 SonarLint 탭을 열고 왼쪽 위에 초록색 화살표를 클릭해주면 해당 파일을 확인해 줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1869&quot; data-origin-height=&quot;967&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlpPKC/btq8HGmiQZq/sf8kLtfmuLRk26UZDKIVS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlpPKC/btq8HGmiQZq/sf8kLtfmuLRk26UZDKIVS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlpPKC/btq8HGmiQZq/sf8kLtfmuLRk26UZDKIVS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlpPKC%2Fbtq8HGmiQZq%2Fsf8kLtfmuLRk26UZDKIVS0%2Fimg.png&quot; data-origin-width=&quot;1869&quot; data-origin-height=&quot;967&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;저의 경우 &quot;.com/&quot; 이라는 문자열이 4번 반복 사용되었다고 알려주네요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;63&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd0d1n/btq8IC46rMR/7hhF9Xi82pVVkGxST2eVb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd0d1n/btq8IC46rMR/7hhF9Xi82pVVkGxST2eVb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd0d1n/btq8IC46rMR/7hhF9Xi82pVVkGxST2eVb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd0d1n%2Fbtq8IC46rMR%2F7hhF9Xi82pVVkGxST2eVb0%2Fimg.png&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;63&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 오른쪽을 보면 아래처럼 문제가 되는 부분을 어떻게 고쳐야하는지도 알려줍니다. ㅎ&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;764&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crq1ij/btq8H4N0tzl/fHEKggiBygUuW2SABHgKN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crq1ij/btq8H4N0tzl/fHEKggiBygUuW2SABHgKN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crq1ij/btq8H4N0tzl/fHEKggiBygUuW2SABHgKN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrq1ij%2Fbtq8H4N0tzl%2FfHEKggiBygUuW2SABHgKN1%2Fimg.png&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;764&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정해야할 부분이 없다면 아래처럼 뜨게 됩니다. 물론 무조건 수정해야하는 것은 아닙니다. 아래처럼 예외의 상황에는 수정 하지 않아도 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;186&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSweGN/btq8KaNSX69/wTGJikgqB3gxJ1s2yqHNX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSweGN/btq8KaNSX69/wTGJikgqB3gxJ1s2yqHNX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSweGN/btq8KaNSX69/wTGJikgqB3gxJ1s2yqHNX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSweGN%2Fbtq8KaNSX69%2FwTGJikgqB3gxJ1s2yqHNX0%2Fimg.png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;186&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;163&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjUMbt/btq8KbssOr4/6e7fFEsXkLuC8Z49VneKxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjUMbt/btq8KbssOr4/6e7fFEsXkLuC8Z49VneKxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjUMbt/btq8KbssOr4/6e7fFEsXkLuC8Z49VneKxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjUMbt%2Fbtq8KbssOr4%2F6e7fFEsXkLuC8Z49VneKxk%2Fimg.png&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;163&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;아래처럼 노란색과 파란색이 있는데 각각 minor, info로 상대적으로 낮은 수준의 체크라고 생각하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 고치는게 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;23&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SExca/btq8G42DgxX/RO4IKcVMCHIZT8pVym8GmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SExca/btq8G42DgxX/RO4IKcVMCHIZT8pVym8GmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SExca/btq8G42DgxX/RO4IKcVMCHIZT8pVym8GmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSExca%2Fbtq8G42DgxX%2FRO4IKcVMCHIZT8pVym8GmK%2Fimg.png&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;23&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;72&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NRWRq/btq8HsWwImk/OcGyCQ60C69zFNZC8qXtPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NRWRq/btq8HsWwImk/OcGyCQ60C69zFNZC8qXtPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NRWRq/btq8HsWwImk/OcGyCQ60C69zFNZC8qXtPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNRWRq%2Fbtq8HsWwImk%2FOcGyCQ60C69zFNZC8qXtPK%2Fimg.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;72&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SonarQube&lt;/b&gt;&lt;span style=&quot;color: #596172;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로젝트를 호스팅하고 분석을 실행할 수있는 서버 인 반면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;SonarLint&lt;/b&gt;&lt;span style=&quot;color: #596172;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;우리가 이것과 연결할 수있게 해주는 에이전트입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;SonarQube&lt;/b&gt;&lt;span style=&quot;color: #596172;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;원격으로 분석을 실행합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;SonarLint&lt;/b&gt;&lt;span style=&quot;color: #596172;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;IDE와 함께 사용하거나 CLI 명령을 통해 실행할 수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 제대로된 정적 코드 분석을 위해서는 &lt;b&gt;SonarLint&lt;span&gt;&amp;nbsp;&lt;/span&gt;플러그인과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SonarQube서버를 연결하여 써야합니다.&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;저는 AWS EC2에 소나큐브를 다운받고 연결하려고 하였으나 프리티어 수준의 서버에서는 도커로 SonarQube를 실행할려고 하니 서버가 죽어버리더군요.&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;차후에 개인 컴퓨터나 EC2(성능 괜찮은) 를 사용해서 적용해 보려고합니다 .ㅎ&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;PS.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;157&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGLMpl/btq8L65iMd1/ifd3F2RNSBiPxM4GsHbkbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGLMpl/btq8L65iMd1/ifd3F2RNSBiPxM4GsHbkbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGLMpl/btq8L65iMd1/ifd3F2RNSBiPxM4GsHbkbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGLMpl%2Fbtq8L65iMd1%2Fifd3F2RNSBiPxM4GsHbkbk%2Fimg.png&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;157&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;이 작은 프로젝트에 33개 파일을 수정해야하다니 ..... 죄책감이 밀려오네요.... 후 ..&lt;/p&gt;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/246</guid>
      <comments>https://wellbell.tistory.com/246#entry246comment</comments>
      <pubDate>Sat, 3 Jul 2021 21:37:38 +0900</pubDate>
    </item>
    <item>
      <title>코드 리뷰 조금 이해하기</title>
      <link>https://wellbell.tistory.com/245</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이글은&lt;/p&gt;
&lt;p id=&quot;kakaoBody&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tech.kakao.com/2016/02/04/code-review/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;카카오스토리 팀의 코드 리뷰 도입 사례 &amp;ndash; 코드 리뷰, 어디까지 해봤니?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 글을 읽고 &lt;b&gt;제 맘대로 정리한 글입니다.ㅎ&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;팀은 문화로 코드 리뷰를 하지않았고 사실 서로간의 의견을 나누기보다 아 그렇군아 하면서 바꾸는 수준이었다.&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;&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 id=&quot;리뷰-없이는-머지되지-않도록-제한&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;리뷰 없이는 머지되지 않도록 제한&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;master, develop 브랜치로는 바로 푸시하지 못하도록 push 깃훅 추가&lt;/li&gt;
&lt;li&gt;프로젝트를 클론할 때, 깃훅을 반드시 설치하도록 강제함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;컨벤션 리뷰로 부터 벗어나기 위해 commit 깃훅에 린트 검사 적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;조금은-불필요한-논쟁이라고-생각했던-리뷰들&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;조금은 불필요한 논쟁이라고 생각했던 리뷰들 지양&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;취향의 차이 (if vs switch)&lt;/li&gt;
&lt;li&gt;바꾸기도 뭐하고 안 바꾸기도 뭐한 애매한 수준의 변수명 제안&lt;/li&gt;
&lt;li&gt;아주 미묘한 성능 개선 제안&lt;/li&gt;
&lt;li&gt;너무 먼 미래에 대한 방어 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 리뷰가 병목 -&amp;gt; 브랜치 미리보기 서버&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공유하고자 하는 브랜치를 특정 패턴의 이름으로 푸시하면, 해당 기능의 스냅샷이 배포되도록 서버를 구성함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;주기적으로&amp;nbsp;리뷰를&amp;nbsp;독려하고&amp;nbsp;머지를&amp;nbsp;담당하는&amp;nbsp;&amp;lsquo;리뷰&amp;nbsp;마스터&amp;rsquo;&amp;nbsp;제도&amp;nbsp;도입&lt;/b&gt; &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #444444;&quot;&gt;리뷰 마스터는 한 주 또는 특정 주기로 쌓여있는 PR을 빨리 리뷰하도록 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #444444;&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;개인 판단 하에 빠르게 머지 가능한 것은 머지&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;스펙이 큰 피처인 경우엔 피처를 분리 하여 태스크 단위의 리뷰 진행&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;피처의 큰 줄기인 feature/A 를 따고 하위 태스크를 feature/A-1, feature/A-2 로 땀&lt;/li&gt;
&lt;li&gt;담당자들이 우선 1차 리뷰 진행하고 상위 피처로 pull 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;유연해진-리뷰&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;유연한 리뷰&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단한 건은 한 사람도 바로 머지 가능&lt;/li&gt;
&lt;li&gt;마크업 수정, 코드 변경 단위가 적고 큰 영향이 없는 건&lt;/li&gt;
&lt;li&gt;PR이 올라온 지 오래된 리뷰는, 빨리 머지하자라는 분위기가 만들어짐&lt;/li&gt;
&lt;li&gt;개인적으로도, 이런 건은 큰 문제가 없다면 자세한 리뷰보다는 빠른 머지가 더 의미있다고 생각함&lt;/li&gt;
&lt;li&gt;좋게 보면 유연해졌지만, 나쁘게 보면 좀 대충 대충 느낌도 있음&lt;/li&gt;
&lt;li&gt;머지 사이클도 빨라져서, 리뷰가 병목이 되는 상황은 조금 줄어들었음&lt;/li&gt;
&lt;li&gt;복합적인데, 배포 비용도 적고 주기도 짧아서, 쉽게 수정해서 배포할 수 있다는 생각이 근저에 깔려있기 때문인 듯도 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;한정적인-리뷰-시간-각자의-리뷰-포인트가-있음&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;한정적인 리뷰 시간, 각자의 리뷰 포인트를 둠&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포도 잦아지고 처리해야할 작업도 많아지면서, 리뷰 시간이 갈수록 줄어들음&lt;/li&gt;
&lt;li&gt;코드를 다 볼 수 없으니, 중요하다고 생각되는 포인트들만 리뷰하게 됨&lt;/li&gt;
&lt;li&gt;변수명이나 컨벤션 리뷰는 거의 안하게 됨&lt;/li&gt;
&lt;li&gt;나는 주로 아래 항목 위주로만 리뷰함&lt;/li&gt;
&lt;li&gt;예외 처리가 잘 되어 있는지 (예: 컨텐츠가 없다거나, 유효하지 않은 파라미터라거나)&lt;/li&gt;
&lt;li&gt;좀 더 간결하게 작성할 수 있는 방법이 있는지&lt;/li&gt;
&lt;li&gt;버그가 발생할 가능성이 있는 코드인지&lt;/li&gt;
&lt;li&gt;병목이 될 가능성이 있는 코드인지&lt;/li&gt;
&lt;li&gt;빌드/배포 관점에서 (이건 내가 주로 작업했던 부분이라)&lt;/li&gt;
&lt;li&gt;다른 멤버들도 따로 얘기하진 않았지만, 비슷하게 각자의 포인트를 잡고 리뷰하는 듯 함&lt;/li&gt;
&lt;li&gt;예) 누구는 성능 위주로, 누구는 프레임워크 위주로, 누구는 크로스브라우징 위주로, 누구는 모바일 위주로 등등&lt;/li&gt;
&lt;/ul&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;후기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 리뷰 문화의 성공은 결국 팀원 모두가 코드 리뷰가 필수적인 것으로 받아 드릴 수 있을때 가능한 것이 아닐까 라는 생각을 하게 됩니다. ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://tech.kakao.com/2016/02/04/code-review/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tech.kakao.com/2016/02/04/code-review/&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>개발 공부/Clean Code</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/245</guid>
      <comments>https://wellbell.tistory.com/245#entry245comment</comments>
      <pubDate>Fri, 2 Jul 2021 22:30:46 +0900</pubDate>
    </item>
    <item>
      <title>Quotes, Variables, Functions 개념 및 사용</title>
      <link>https://wellbell.tistory.com/244</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Quotes&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shell 에서 두 번째로 중요한 개념은 quotes 이라고 할 수 있습니다. shell 에서 quotes 은 숫자나 스트링 값을 구분하기 위한 용도로 사용하지 않습니다. 123, &quot;123&quot;, '123' 은 모두 같고 abc, &quot;abc&quot;, 'abc' 들은 차이가 없으며 모두 다 shell 에서는 스트링입니다.&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;br /&gt;( sed, awk 스크립트를 quotes 을 이용해 작성하는 이유가 하나의 인수로 만들기 위해서입니다. )&lt;/li&gt;
&lt;li&gt;라인 개행이나 둘 이상의 공백을 유지하기 위해&lt;/li&gt;
&lt;li&gt;단어분리,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;globbing&amp;nbsp;&lt;/span&gt;발생을 방지하기 위해&lt;/li&gt;
&lt;li&gt;shell 키워드, 메타문자, alias 와 같이 shell 에서 특수기능을 하는 문자, 단어를 단순히 명령문의 스트링으로 만들기위해&lt;/li&gt;
&lt;li&gt;문자 그대로 스트링을 강조하기 위해&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;variables&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Variables&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름은 알파벳 (대, 소문자), 숫자,&lt;span&gt;&amp;nbsp;&lt;/span&gt;_&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 만들 수 있으며 이름의 첫 문자로 숫자가 올 수 없습니다. 변수이름을 대문자로 사용할때는 shell 환경변수와 중복되지 않도록 주의해야 합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625227039357&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ORIGINAL_FILE=myfile

FILE_PREFIX=$ORIGINAL_FILE.split        # '.' 사용
FILE_PREFIX=$ORIGINAL_FILE-split        # '-' 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 shell 에 정의된 모든 변수명 보기&lt;/p&gt;
&lt;pre id=&quot;code_1625227073909&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ compgen -A variable

# 현재 shell 에 특정 변수가 설정되어 있는지 간단히 확인하는 방법
# 가령 대문자 B 로 시작하는 변수를 조회하려면
$ $B[tab]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의된 변수 삭제하기&lt;/p&gt;
&lt;pre id=&quot;code_1625227095562&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# unset 명령 실행시 옵션을 주지 않으면 첫번째로 변수를 unset 시도하고
# 실패할 경우 함수를 unset 시도합니다.
$ unset 변수명&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 id=&quot;functions&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Functions&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{ ;},&lt;span&gt;&amp;nbsp;&lt;/span&gt;( )&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 이용해 명령 그룹을 만들게 되면 같은 context 에서 실행이 됩니다. 이것은 명령 그룹 전체가 하나의 명령처럼 실행되는 것과 같은 효과가 있습니다. 따라서 앞에 함수명을 붙여서 함수 정의를 하게 되면 일반 명령과 동일하게 사용될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625230042887&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 명령 그룹을 하나의 짧은 이름으로 사용 ( 함수정의 ) 
{ 
    read var1
    read var2
    echo &quot;$var1 $var2&quot;
} &amp;lt; infile

f1() { 
    read var1
    read var2
    echo &quot;$var1 $var2&quot;
}           

$ f1 &amp;lt; infile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수를 실행할 땐 먼저 정의가 되어 있어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625230133539&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foo1            # 여기서는 foo1 함수 정의가 안되어있기 때문에 실행할 수 없다.

foo1() {
    echo &quot;foo1&quot;
    foo2        # foo1 함수를 실행한 곳이 foo2 아래이므로 실행 가능
}

foo2() {
    echo &quot;foo2&quot;
}

foo1            # 여기서는 foo1 함수를 실행할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;조건에 따라 다른 함수를 정의할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625230150033&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if test -n &quot;$KSH_VERSION&quot;; then
    puts() {
        print -r -- &quot;$*&quot;
    }
else
    puts() {
        printf '%s\n' &quot;$*&quot;
    }
fi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에서 연산 결과를 리턴하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 함수에서도 외부 명령과 동일하게 명령치환&amp;nbsp;을 사용&lt;/p&gt;
&lt;pre id=&quot;code_1625230249169&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;f1() { expr $1 + $2 ;}
f2() { date &quot;+%Y&quot; ;}
f3() { echo &quot;hello $1&quot; ;}

-------------------------

$ AA=$(f1 1 2)
$ echo $AA
3

$ AA=`f2`
$ echo $AA
2015

$ AA=$(f3 world)
$ echo $AA
hello world&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Shell Script/Bash Shell</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/244</guid>
      <comments>https://wellbell.tistory.com/244#entry244comment</comments>
      <pubDate>Fri, 2 Jul 2021 21:52:59 +0900</pubDate>
    </item>
    <item>
      <title>Shell Script 간단 주의점</title>
      <link>https://wellbell.tistory.com/243</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일명 : NUL, / 제외하고 모두 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;command 에서 공백을 통해서 인수를 구분하게 됨&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625225103027&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;command arg1 arg2 arg3 ...

/ex
$ [10 -eq 10 ]; echo $? // 10: command not found

$ [ 10 -eq 10]; echo $? // bash: [: missing ']'

$ [ 10 -eq 10 ]; echo $? // &amp;lt;&amp;lt;&amp;lt; 이렇게 사용해야함

$ [ a=b ]; echo $? // 0

$ [ a = b ]; echo $? // 1 &amp;lt;&amp;lt;&amp;lt; 이렇게 사용해야함
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대입연산은 명령 구조를 가지지 않음&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625225323773&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 변수 AA 가 명령이 되고 = , 10 는 각각 인수로 인식됩니다. 
$ AA = 10
AA: command not found

# 그러므로 shell 에서 대입연산은 반드시 공백 없이 붙여 사용해야 합니다.
$ AA=10
$ echo $AA
10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;shell script에서 0 = , 나머지 숫자 = 거짓&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;shell script 에서 return은 연산 결과 가 아닌 종료 상태 값을 지정함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개행을 자동 인식하여 명령 종료 문자인 ;는 필수가 아님 하지만 개행을 하지않고 한 줄로 사용할 경우 ;사용 해야함&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625226390076&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 명령들을 한줄에 연이어 쓸 경우는 ';' 를 붙여야 한다.
$ for i in {1..3} do echo $i done
&amp;gt; 오류

$ for i in {1..3}; do echo $i; done
1
2
3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;shell 메타 문자는 특별 취급을 받아 공백, ;의 제약을 받지 않음.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625226446174&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; ( )   `   |   &amp;amp;   ;
 &amp;amp;&amp;amp;  ||                            # AND, OR 문자
 &amp;lt;   &amp;gt;   &amp;gt;&amp;gt;                        # redirection 문자
 *   ?   [ ]                       # glob 문자
 &quot;   '                             # quote 문자
 \   $      
 =   +=                            # 대입연산&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1625226436778&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# '(' 와 명령 사이에 공백을 두지 않아도 되고 ')' 앞에 ; 를 붙이지 않아도 된다.
$ (echo hello; echo world)
hello
world&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;escape&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Escape&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Shell 에서 사용되는 명령문에는 단지 명령문을 위한 스트링만 존재하지 않습니다. script 작성을 위해 shell 에서 제공하는 키워드, 메타문자, glob 문자들이 같이 사용되는 환경이기 때문에 만약에 &lt;b&gt;명령문에서 동일한 문자가 사용된다면 escape 하거나 quote 하여 명령문을 위한 스트링으로 만들어 줘야 오류가 발생하지 않습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1625226522292&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 명령문에 shell 에서 사용하는 glob 문자 '*' 가 포함되어 에러 발생
$ expr 3 * 4       
expr: syntax error

# 다음과 같이 escape 하거나 quote 하여 명령문을 위한 스트링으로 만들어줌
$ expr 3 \* 4       
$ expr 3 '*' 4
12&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수나 명령치환을 quote 하지 않으면 값이 출력될때 IFS ( Internal Field Separator : 기본적으로 공백문자로 구성 ) 에의해 단어가 분리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 뜻하지 않게 인수가 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;Shell 에서는 파일을 select 할때 glob 문자를 (&amp;nbsp;*,?,[ ]&amp;nbsp;) 이용합니다. 그러므로 변&lt;b&gt;수나 명령치환을 quote 하지 않고 사용할 경우 출력되는 값에 glob 문자가 포함되면 뜻하지 않게 globbing 이 발생해 오류가 발생&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625226612166&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ AA=&quot;User-Agent: *&quot;     # 변수 AA 값으로 glob 문자 '*' 가 사용됨

$ echo &quot;$AA&quot;             # quote 을 하면 globbing 이 발생하지 않음
User-Agent: *

$ echo $AA               # quote 을 하지 않아 globbing 이 발생해 뜻하지 않은 값이 출력됨
User-Agent: 2013-03-19 154412.csv Address.java address.ser 
ReadObject.class ReadObject.java robots.txt 쉘 스크립트 테스팅.txt 
WriteObject.class WriteObject.java&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;cd-명령은-종료-상태-값을-확인해야-한다&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cd 명령은 종료 상태 값을 확인해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625226691666&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# cd 명령 실패시 현 디렉토리 모두 삭제됨
cd ~/tempdir
rm -rf *

# 이동이 실패시 삭제 명령이 실행되지 않음
cd ~/tempdir &amp;amp;&amp;amp; rm -rf *&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;child 프로세스에서도 변수를 사용하려면 export 해야 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정한 변수값은 subshell 프로세스에서는 그대로 사용할 수 있지만 스크립트 파일을 실행할 때와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://mug896.github.io/bash-shell/process_creation.html#exec&quot;&gt;exec&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에 의해 생성되는 프로세스에서는 사용할 수 없습니다. 이때는 변수를 export 해야 사용할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mug896.github.io/bash-shell/basics.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mug896.github.io/bash-shell/basics.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>Shell Script/Bash Shell</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/243</guid>
      <comments>https://wellbell.tistory.com/243#entry243comment</comments>
      <pubDate>Fri, 2 Jul 2021 20:56:27 +0900</pubDate>
    </item>
    <item>
      <title>15. jacoco를 통한 테스트 커버리지 체크</title>
      <link>https://wellbell.tistory.com/242</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 커버리지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #303a3e;&quot;&gt;코드 커버리지는 소프트웨어의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;테스트 케이스가 얼마나 충족되었는지를 나타내는 지표&lt;/b&gt;&lt;span style=&quot;color: #303a3e;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 하나이다. 테스트를 진행하였을 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;lsquo;코드 자체가 얼마나 실행되었느냐&amp;rsquo;&lt;/b&gt;&lt;span style=&quot;color: #303a3e;&quot;&gt;는 것이고, 이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;수치&lt;/b&gt;&lt;span style=&quot;color: #303a3e;&quot;&gt;를 통해 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #303a3e;&quot;&gt;jacoco&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java code coverage의 약자로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;junit 테스트의 결과를 바탕으로 커버리지를 결과를 리포트 해주는 툴 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 코드정적분석도구인 sonarqube와 연계하여 사용하는 경우가 많습니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. build.gradle에 jacoco 추가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- plugins에 아래처럼 추가&lt;/p&gt;
&lt;pre id=&quot;code_1625050709138&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	...
	id 'jacoco'
}

...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가한 뒤 gradle reload를 진행해주면 verification 에 아래 와 같이 추가됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;345&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9XFG/btq8wyuIXVD/eZ0BbilvwVkayB7aZbkn91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9XFG/btq8wyuIXVD/eZ0BbilvwVkayB7aZbkn91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9XFG/btq8wyuIXVD/eZ0BbilvwVkayB7aZbkn91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9XFG%2Fbtq8wyuIXVD%2FeZ0BbilvwVkayB7aZbkn91%2Fimg.png&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;345&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. gradle test 진행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위 사진의 test를 더블 클릭하셔도 되고 gradle test 명령어로 하셔도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- test 진행이 완료 되면 build &amp;gt; test 폴더 안에 index.html을 발견하실 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;386&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7SKSO/btq8v7RXYES/3kwM7kwqzDBRa1kWBF5Vp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7SKSO/btq8v7RXYES/3kwM7kwqzDBRa1kWBF5Vp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7SKSO/btq8v7RXYES/3kwM7kwqzDBRa1kWBF5Vp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7SKSO%2Fbtq8v7RXYES%2F3kwM7kwqzDBRa1kWBF5Vp1%2Fimg.png&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;386&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 끌어다가 띄우고 크롬을 통해서 열면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;308&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFTDMy/btq8uIL94L4/bx8rcUbbSFxD2lFruWj4k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFTDMy/btq8uIL94L4/bx8rcUbbSFxD2lFruWj4k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFTDMy/btq8uIL94L4/bx8rcUbbSFxD2lFruWj4k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFTDMy%2Fbtq8uIL94L4%2Fbx8rcUbbSFxD2lFruWj4k1%2Fimg.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;308&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;- 이런 화면을 확인 하실 수 있습니다. 이건 jacoco가 아니라 그냥 test 결과입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이제 jacoco 리포트를 확인해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;617&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ELqs7/btq8xDI8bYQ/St8RImIzX2b3MRHaCZZxrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ELqs7/btq8xDI8bYQ/St8RImIzX2b3MRHaCZZxrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ELqs7/btq8xDI8bYQ/St8RImIzX2b3MRHaCZZxrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FELqs7%2Fbtq8xDI8bYQ%2FSt8RImIzX2b3MRHaCZZxrk%2Fimg.png&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;617&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. jacocoTestReport 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- jacocoTestReport를 더블클릭해서 실행해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;337&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GTa93/btq8v2C5pf7/ScBVHZGxMS3bCliOFsiHt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GTa93/btq8v2C5pf7/ScBVHZGxMS3bCliOFsiHt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GTa93/btq8v2C5pf7/ScBVHZGxMS3bCliOFsiHt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGTa93%2Fbtq8v2C5pf7%2FScBVHZGxMS3bCliOFsiHt1%2Fimg.png&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;337&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;- build &amp;gt; report &amp;gt; jacoco 폴더 안에서 index.html을 확인 하실 수 있습니다. 열어줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;832&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6ugwM/btq8xEVzB23/xt5wFxy2RHMIT2U9nmaKSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6ugwM/btq8xEVzB23/xt5wFxy2RHMIT2U9nmaKSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6ugwM/btq8xEVzB23/xt5wFxy2RHMIT2U9nmaKSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6ugwM%2Fbtq8xEVzB23%2Fxt5wFxy2RHMIT2U9nmaKSk%2Fimg.png&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;832&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 결과 확인&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;- 프로젝트 중에 mockito를 이용해서 단위 테스트를 진행하면서 신경을 제일 쓴 service 테스트인대 어디를 안타는 거지 했습니다....&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;163&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGZTk0/btq8wJb04FC/7vjC4ITFqvrNjbMjc1Wwbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGZTk0/btq8wJb04FC/7vjC4ITFqvrNjbMjc1Wwbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGZTk0/btq8wJb04FC/7vjC4ITFqvrNjbMjc1Wwbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGZTk0%2Fbtq8wJb04FC%2F7vjC4ITFqvrNjbMjc1Wwbk%2Fimg.png&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;163&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;- 생각해보니 파일 업로드 등 파일 관련 처리를 s3로 옴기고 나서 테스트 코드를 추가하지 않았다는걸 알게되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(추가해야지 .....ㅠㅠㅠㅠ)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;317&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6zssR/btq8xLUFLOV/kSqkKbSZPo9hKDiBlOjME0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6zssR/btq8xLUFLOV/kSqkKbSZPo9hKDiBlOjME0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6zssR/btq8xLUFLOV/kSqkKbSZPo9hKDiBlOjME0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6zssR%2Fbtq8xLUFLOV%2FkSqkKbSZPo9hKDiBlOjME0%2Fimg.png&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;317&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;- 27%? 이게 뭐지 라는 생각으로 확인 해보니 (참혹한 dto 테스트 커버리지 .....)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;113&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b33qD4/btq8wzUJeh0/ljJ1YE4emuyl92vjNKyEa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b33qD4/btq8wzUJeh0/ljJ1YE4emuyl92vjNKyEa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b33qD4/btq8wzUJeh0/ljJ1YE4emuyl92vjNKyEa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb33qD4%2Fbtq8wzUJeh0%2FljJ1YE4emuyl92vjNKyEa1%2Fimg.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;113&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;- Dto에 롬복을 통해서 @Data를 통해서 setter 생성하고 한번도 안써서 생긴 내용 이었습니다 .ㅎ (@Builder 짱짱맨.ㅎ)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7tTrl/btq8vvlez7q/mnHD300JvbK4gudG6OIAnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7tTrl/btq8vvlez7q/mnHD300JvbK4gudG6OIAnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7tTrl/btq8vvlez7q/mnHD300JvbK4gudG6OIAnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7tTrl%2Fbtq8vvlez7q%2FmnHD300JvbK4gudG6OIAnk%2Fimg.png&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;684&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;- dto 테스트를 추가하면&lt;/p&gt;
&lt;pre id=&quot;code_1625052785963&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class ProjectResponseDtoTest {

    @Test
    void projectResponseDtoTest() {
        ProjectResponseDto projectResponseDto1 = new ProjectResponseDto();
        projectResponseDto1.setProjectId(1L);
        projectResponseDto1.setProjectTitle(&quot;이름&quot;);
        projectResponseDto1.setProjectContent(&quot;내용&quot;);
        projectResponseDto1.setProjectPostScript(&quot;추신&quot;);
        projectResponseDto1.setFileUrl(&quot;파일 주소&quot;);
        projectResponseDto1.setFileOriginName(&quot;파일 이름&quot;);
        projectResponseDto1.setProjectLink(&quot;프로젝트 링크&quot;);
        projectResponseDto1.setLevel(1);
        projectResponseDto1.setMemberId(1L);

        ProjectResponseDto projectResponseDto2 = ProjectResponseDto.builder()
                .projectId(1L)
                .projectTitle(&quot;이름&quot;)
                .projectContent(&quot;내용&quot;)
                .projectPostScript(&quot;추신&quot;)
                .fileUrl(&quot;파일 주소&quot;)
                .fileOriginName(&quot;파일 이름&quot;)
                .projectLink(&quot;프로젝트 링크&quot;)
                .level(1)
                .memberId(1L)
                .build();

        assertAll(
                () -&amp;gt; assertThat(projectResponseDto1).isEqualTo(projectResponseDto2),
                () -&amp;gt; assertThat(projectResponseDto1.toString())
                        .hasToString(&quot;ProjectResponseDto(projectId=1, projectTitle=이름, projectContent=내용,&quot; +
                                &quot; projectPostScript=추신, fileUrl=파일 주소, fileOriginName=파일 이름,&quot; +
                                &quot; projectLink=프로젝트 링크, level=1, memberId=1)&quot;)
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테스트 커버리지가 올라가긴합니다만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 과연 dto를 테스트하는게 의미가 있는지 위와 같은 코드를 작성하는 게 의미가 있는지 등을 생각하게 되면서 jacoco 설정을 찾아보게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저의 경우 dto와 querydsl에 의해 생기는 Q클래스를 제외하도록 특정 디렉토리의 특정 클래스를 제외시키는 방법을 사용하였습니다.( 제가 사용한 방법과 다른 방법 등은 아래 주소에서 확인하실 수 있습니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1625055021943&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// build.gradle 에 추가

jacocoTestReport {
	dependsOn test // tests are required to run before generating the report

	afterEvaluate {
		classDirectories.setFrom(files(classDirectories.files.collect {
			fileTree(dir: it, exclude: [
					&quot;com/myintroduce/web/dto/*&quot;,
					&quot;**/Q*.class&quot;
			])
		}))
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아본 결과 디렉토리, 제외, 최소 테스트 커버리지 % 설정 등 다양한 설정이 가능하다는 것을 알게 되었습니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;후기&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/jacoco-report-exclude&quot;&gt;https://www.baeldung.com/jacoco-report-exclude&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoReport.html&quot;&gt;https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoReport.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://techblog.woowahan.com/2661/&quot;&gt;https://techblog.woowahan.com/2661/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/242</guid>
      <comments>https://wellbell.tistory.com/242#entry242comment</comments>
      <pubDate>Wed, 30 Jun 2021 21:21:41 +0900</pubDate>
    </item>
    <item>
      <title>Java의 직렬화(Serialize)</title>
      <link>https://wellbell.tistory.com/241</link>
      <description>&lt;h2 id=&quot;직렬화serialize&quot; data-ke-size=&quot;size26&quot;&gt;직렬화(Serialize)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술.&lt;/li&gt;
&lt;li&gt;JVM(Java Virtual Machine 이하 JVM)의 메모리에 상주(힙 또는 스택)되어 있는 객체 데이터를 바이트 형태로 변환하는 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;역직렬화deserialize&quot; data-ke-size=&quot;size26&quot;&gt;역직렬화(Deserialize)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;byte로 변환된 Data를 원래대로 Object나 Data로 변환하는 기술을 역직렬화(Deserialize)라고 부릅니다.&lt;/li&gt;
&lt;li&gt;직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태.&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;직렬화방법에는 여러 Format이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표형태의 다량의 데이터를 직렬화할때는 CSV형태&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1624718782207&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member member = new Member(&quot;김배민&quot;, &quot;deliverykim@baemin.com&quot;, 25);
  // member객체를 csv로 변환 
  String csv = String.format(&quot;%s,%s,%d&quot;,member.getName(), member.getEmail(), member.getAge()); 
  System.out.println(csv);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조적인 데이터는 XML, JSON형태.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #212121;&quot;&gt;&lt;span&gt;&amp;nbsp;no 라이브러리&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624718801228&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Member member = new Member(&quot;김배민&quot;, &quot;deliverykim@baemin.com&quot;, 25);
  // member객체를 json으로 변환 
  String json = String.format(
          &quot;{\&quot;name\&quot;:\&quot;%s\&quot;,\&quot;email\&quot;:\&quot;%s\&quot;,\&quot;age\&quot;:%d}&quot;,
          member.getName(), member.getEmail(), member.getAge());
  System.out.println(json);        &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Jackson&lt;/p&gt;
&lt;pre id=&quot;code_1624719040828&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 직렬화
ObjectMapper mapper = new ObjectMapper(); 
String jsonResult = mapper.writeValueAsString(json으로 바꾸고싶은 java객체);

// 역직렬화
String jsonInput = &quot;json 데이터&quot;;
ObjectMapper mapper = new ObjectMapper();
Example exam = mapper.readValue(jsonInput, Example.class);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Gson&lt;/p&gt;
&lt;pre id=&quot;code_1624719072366&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 직렬화
String jsonResult = new Gson().toJson(json으로 바꾸고싶은 java객체);

// 역직렬화
String jsonInput = &quot;json 데이터&quot;;
Example exam = new Gson().fromJson(jsonInput, Example.class);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 id=&quot;어디에-사용되는가&quot; data-ke-size=&quot;size26&quot;&gt;어디에 사용되는가?&lt;/h2&gt;
&lt;h3 id=&quot;서블릿-세션-servlet-session&quot; data-ke-size=&quot;size23&quot;&gt;서블릿 세션 (Servlet Session)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세션을 서블릿 메모리 위에서 운용한다면 직렬화를 필요로 하지 않지만, 파일로 저장하거나 세션 클러스터링, DB를 저장하는 옵션 등을 선택하게 되면 세션 자체가 직렬화가 되어 저장되어 전달됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;캐시-cache&quot; data-ke-size=&quot;size23&quot;&gt;캐시 (Cache)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ehcache, Redis, Memcached 라이브러리 시스템을 많이 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;자바-rmiremote-method-invocation&quot; data-ke-size=&quot;size23&quot;&gt;자바 RMI(Remote Method Invocation)&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;/ul&gt;
&lt;h2 id=&quot;자바의-직렬화-단점&quot; data-ke-size=&quot;size26&quot;&gt;자바의 직렬화 단점?&lt;/h2&gt;
&lt;h4 id=&quot;역직렬화시-클래스-구조-변경-문제&quot; data-ke-size=&quot;size20&quot;&gt;1. 역직렬화시 클래스 구조 변경 문제&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;역직렬화를 진행하면 java.io.InvalidClassException&lt;span style=&quot;color: #555555;&quot;&gt;이 발생&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 막기 위해서 &lt;span style=&quot;color: #555555;&quot;&gt;모델의 버젼간의 호환성을 유지하기 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;SUID(serialVersionUID)&lt;span style=&quot;color: #555555;&quot;&gt;를 정의해야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;pre id=&quot;code_1624718698072&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = -5129628467395047900L;
    
    String name;
    int age;
    double height;
    String hobby;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;직렬화-data-size-문제&quot; data-ke-size=&quot;size20&quot;&gt;2. 직렬화 Data Size 문제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;간단한 객체의 내용도 직렬화시 2배이상의 차이 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;- &lt;span style=&quot;color: #555555;&quot;&gt;메모리기반의 Cache에서는 Data를 저장할 수 있는 용량의 한계가 있기 때문에 Json 형태와 같은 경량화된 형태로 직렬화하는 것도 좋은 방법&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;정리&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 저장소로 저장되는 데이터는 짧은 만료시간의 데이터를 제외하고 자바 직렬화를 사용을 지양합니다.&lt;/li&gt;
&lt;li&gt;역직렬화시 반드시 예외가 생긴다는 것을 생각하고 개발합니다.&lt;/li&gt;
&lt;li&gt;자주 변경되는 비즈니스적인 데이터를 자바 직렬화을 사용하지 않습니다.&lt;/li&gt;
&lt;li&gt;긴 만료 시간을 가지는 데이터는 JSON 등 다른 포맷을 사용하여 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 경험&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Framework(boot, webflux)과 Redis 연동&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- String, String&lt;/p&gt;
&lt;pre id=&quot;code_1624719220394&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class BasicRedisConfig {

    ...

    @Primary
    @Bean
    ReactiveRedisOperations&amp;lt;String, String&amp;gt; redisOperations(ReactiveRedisConnectionFactory factory) {
        RedisSerializer&amp;lt;String&amp;gt; serializer = new StringRedisSerializer();
        RedisSerializationContext&amp;lt;String, String&amp;gt; serializationContext = RedisSerializationContext
                .&amp;lt;String, String&amp;gt;newSerializationContext()
                .key(serializer)
                .value(serializer)
                .hashKey(serializer)
                .hashValue(serializer)
                .build();

        return new ReactiveRedisTemplate&amp;lt;&amp;gt;(factory, serializationContext);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- String, Oject&lt;/p&gt;
&lt;pre id=&quot;code_1624719288344&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class RedisConfig {

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

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

    @Autowired
    public ObjectMapper objectMapper;

    @Bean
    public RedisTemplate&amp;lt;String, Object&amp;gt; redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate&amp;lt;String, Object&amp;gt; redisTemplate = new RedisTemplate&amp;lt;&amp;gt;();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;reference&quot; data-ke-size=&quot;size20&quot;&gt;Reference&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://woowabros.github.io/experience/2017/10/17/java-serialize2.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://woowabros.github.io/experience/2017/10/17/java-serialize2.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>JAVA</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/241</guid>
      <comments>https://wellbell.tistory.com/241#entry241comment</comments>
      <pubDate>Sat, 26 Jun 2021 23:56:45 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 서울에서 김서방 찾기 (자바)</title>
      <link>https://wellbell.tistory.com/240</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12919&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12919&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624714843479&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;String형 배열 seoul의 element중 &amp;quot;Kim&amp;quot;의 위치 x를 찾아, &amp;quot;김서방은 x에 있다&amp;quot;는 String을 반환하는 함수, solution을 완성하세요. seoul에 &amp;quot;Kim&amp;quot;은 오직 한 번만 나타나며 잘못된 값이 입력되는 경우는 없습니&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12919&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12919&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c2Gj4Y/hyKGrZKn0v/SvopkqGrjx1vdfvY3QviC1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/NiDRt/hyKGpVckZP/yGcFTVetitBkxLgaUR4Kg1/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/12919&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12919&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c2Gj4Y/hyKGrZKn0v/SvopkqGrjx1vdfvY3QviC1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/NiDRt/hyKGpVckZP/yGcFTVetitBkxLgaUR4Kg1/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;String형 배열 seoul의 element중 &quot;Kim&quot;의 위치 x를 찾아, &quot;김서방은 x에 있다&quot;는 String을 반환하는 함수, solution을 완성하세요. seoul에 &quot;Kim&quot;은 오직 한 번만 나타나며 잘못된 값이 입력되는 경우는 없습니&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;굉장히 쉬운 코드인데 글을 쓰게 된 이유는 문자열을 다루는데 기억해야하는게 있어서 입니다.&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;1. 문자열을 +로 붙히기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624715238442&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String[] seoul) {
        int kimIdx = getTargetNameIdx(seoul,&quot;Kim&quot;);
        return &quot;김서방은 &quot; + kimIdx + &quot;에 있다&quot;;
    }

    int getTargetNameIdx(String[] names, String targetName) {
        int idx = 0;
        for (String name : names) {
            if (name.equals(targetName)) {
                return idx;
            }
            idx++;
        }
        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;490&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKiveR/btq8bNNLypo/JsNRcXnRkQ0PjeQBERwlUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKiveR/btq8bNNLypo/JsNRcXnRkQ0PjeQBERwlUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKiveR/btq8bNNLypo/JsNRcXnRkQ0PjeQBERwlUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKiveR%2Fbtq8bNNLypo%2FJsNRcXnRkQ0PjeQBERwlUK%2Fimg.png&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;490&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. String.format 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624714899554&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String[] seoul) {
        int kimIdx = getTargetNameIdx(seoul,&quot;Kim&quot;);
        return String.format(&quot;김서방은 %d에 있다&quot;, kimIdx);
    }

    int getTargetNameIdx(String[] names, String targetName) {
        int idx = 0;
        for (String name : names) {
            if (name.equals(targetName)) {
                return idx;
            }
            idx++;
        }
        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;619&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMQcqF/btq8ceYFGzR/zL24MSk5h4DlsL9n5AOGe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMQcqF/btq8ceYFGzR/zL24MSk5h4DlsL9n5AOGe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMQcqF/btq8ceYFGzR/zL24MSk5h4DlsL9n5AOGe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMQcqF%2Fbtq8ceYFGzR%2FzL24MSk5h4DlsL9n5AOGe0%2Fimg.png&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;619&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. StringBuilder 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624715093112&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String[] seoul) {
        int kimIdx = getTargetNameIdx(seoul,&quot;Kim&quot;);
        StringBuilder sb = new StringBuilder();
        sb.append(&quot;김서방은 &quot;);
        sb.append(kimIdx);
        sb.append(&quot;에 있다&quot;);
        return sb.toString();
    }

    int getTargetNameIdx(String[] names, String targetName) {
        int idx = 0;
        for (String name : names) {
            if (name.equals(targetName)) {
                return idx;
            }
            idx++;
        }
        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;511&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bytqG7/btq8bt24E4x/gMXMZInWF3u8Ge3Kus2uCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bytqG7/btq8bt24E4x/gMXMZInWF3u8Ge3Kus2uCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bytqG7/btq8bt24E4x/gMXMZInWF3u8Ge3Kus2uCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbytqG7%2Fbtq8bt24E4x%2FgMXMZInWF3u8Ge3Kus2uCk%2Fimg.png&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;511&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. StringBuffer 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동기화 처리로 인해 StringBuilder 보다 늦음&lt;/p&gt;
&lt;pre id=&quot;code_1624715577162&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String[] seoul) {
        int kimIdx = getTargetNameIdx(seoul,&quot;Kim&quot;);
        return new StringBuffer().append(&quot;김서방은 &quot;).append(kimIdx).append(&quot;에 있다&quot;).toString();
    }

    int getTargetNameIdx(String[] names, String targetName) {
        int idx = 0;
        for (String name : names) {
            if (name.equals(targetName)) {
                return idx;
            }
            idx++;
        }
        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;513&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7Tely/btq8a0yX9lg/YVUA2ZpVc13nYHNmukJWO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7Tely/btq8a0yX9lg/YVUA2ZpVc13nYHNmukJWO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7Tely/btq8a0yX9lg/YVUA2ZpVc13nYHNmukJWO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7Tely%2Fbtq8a0yX9lg%2FYVUA2ZpVc13nYHNmukJWO1%2Fimg.png&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;513&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;3 경우를 보면 StringBuilder가 시간적인 측면에서 압도적인대 IDE에서는 StringBuilder를 String으로 변경가능하다고 표시해줍니다. 그럼 이는 String은 불변 인대 반해 StringBuffer, StringBuilder 는 가변 객체라는 문제가 있습니다. 심지어 StringBuilder 는 동기화 되어 있지않아서 멀티 쓰레드 환경에서 쓰레드 세이프 하지않기도 하죠.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;378&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c19JBu/btq8alczv5I/mtVnrgGzAKBlRjD5DkbuCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c19JBu/btq8alczv5I/mtVnrgGzAKBlRjD5DkbuCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c19JBu/btq8alczv5I/mtVnrgGzAKBlRjD5DkbuCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc19JBu%2Fbtq8alczv5I%2FmtVnrgGzAKBlRjD5DkbuCk%2Fimg.png&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;378&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;사용 추천&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;String &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;:&amp;nbsp;&amp;nbsp;문자열 연산이 적고 멀티쓰레드 환경일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;StringBuffer&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;&amp;nbsp;문자열 연산이 많고 멀티쓰레드 환경일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;StringBuilder&amp;nbsp; &amp;nbsp;&lt;/span&gt;:&amp;nbsp; 문자열 연산이 많고 단일쓰레드이거나 동기화를 고려하지 않아도 되는 경우&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;182&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb9Wb4/btq8dw4MEhW/l8auUmQXql4kXrykUwXPx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb9Wb4/btq8dw4MEhW/l8auUmQXql4kXrykUwXPx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb9Wb4/btq8dw4MEhW/l8auUmQXql4kXrykUwXPx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb9Wb4%2Fbtq8dw4MEhW%2Fl8auUmQXql4kXrykUwXPx0%2Fimg.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;182&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;한가지 더 기억할 것이 위의 표를 보면 String은 String pool 이라는 곳에 등록되어 동일한 String을 생성해도 메모리를 더 잡아먹지 않는 반면 나머지 두개는 만들때마다 메모리에 할당하게 됩니다.&lt;/p&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/240</guid>
      <comments>https://wellbell.tistory.com/240#entry240comment</comments>
      <pubDate>Sat, 26 Jun 2021 22:59:20 +0900</pubDate>
    </item>
    <item>
      <title>Template Method Pattern</title>
      <link>https://wellbell.tistory.com/239</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;215&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbA1AR/btq73bAJJhx/rVoorYFQeHPvrEFv7ZODKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbA1AR/btq73bAJJhx/rVoorYFQeHPvrEFv7ZODKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbA1AR/btq73bAJJhx/rVoorYFQeHPvrEFv7ZODKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbA1AR%2Fbtq73bAJJhx%2FrVoorYFQeHPvrEFv7ZODKK%2Fimg.png&quot; data-origin-width=&quot;215&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전체적으로는&amp;nbsp;동일하면서&amp;nbsp;부분적으로는&amp;nbsp;다른&amp;nbsp;구문으로&amp;nbsp;구성된&amp;nbsp;메서드의&amp;nbsp;코드&amp;nbsp;중복을&amp;nbsp;최소화&amp;nbsp;할&amp;nbsp;때&amp;nbsp;유용&lt;br /&gt;- 동일한 기능을 상위 클래스에서 정의하면서 확장/변화가 필요한 부분만 서브 클래스에서 구현&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AbstractClass&lt;br /&gt;템플릿&amp;nbsp;메서드를&amp;nbsp;정의하는&amp;nbsp;클래스&lt;br /&gt;하위&amp;nbsp;클래스에&amp;nbsp;공통&amp;nbsp;알고리즘을&amp;nbsp;정의하고&amp;nbsp;하위&amp;nbsp;클래스에서&amp;nbsp;구현될&amp;nbsp;기능을&amp;nbsp;primitive&amp;nbsp;메서드&amp;nbsp;또는&amp;nbsp;hook&amp;nbsp;메서드로&amp;nbsp;정의하는&amp;nbsp;클래스&lt;/li&gt;
&lt;li&gt;ConcreteClass&lt;br /&gt;물려받은&amp;nbsp;primitive&amp;nbsp;메서드&amp;nbsp;또는&amp;nbsp;hook&amp;nbsp;메서드를&amp;nbsp;구현하는&amp;nbsp;클래스&lt;br /&gt;상위 클래스에 구현된 템플릿 메서드의 일반적인 알고리즘에서 하위 클래스에 적합하게 primitive 메서드나 hook 메서드를 오버라이드하는 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;396&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PaDN7/btq70CsoQxa/gnZFBS991p2T6GFn1huXX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PaDN7/btq70CsoQxa/gnZFBS991p2T6GFn1huXX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PaDN7/btq70CsoQxa/gnZFBS991p2T6GFn1huXX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPaDN7%2Fbtq70CsoQxa%2FgnZFBS991p2T6GFn1huXX1%2Fimg.png&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;396&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 부모에서 공통은 구현하고 확장/변화가 필요한 메서드는 선언만 진행&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1624526493546&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    //template method
    public final void play(){

        //initialize the game
        initialize();

        //start game
        startPlay();

        //end game
        endPlay();
    }
}&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1624526509380&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Cricket extends Game{
    @Override
    void initialize() {
        System.out.println(&quot;서킷 경기 준비&quot;);
    }

    @Override
    void startPlay() {
        System.out.println(&quot;서킷 경기 시작&quot;);
    }

    @Override
    void endPlay() {
        System.out.println(&quot;서킷 경기 종료&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1624526522256&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Football extends Game{
    @Override
    void initialize() {
        System.out.println(&quot;축구 경기 준비&quot;);
    }

    @Override
    void startPlay() {
        System.out.println(&quot;축구 경기 시작&quot;);
    }

    @Override
    void endPlay() {
        System.out.println(&quot;축구 경기 종료&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 사용 및 결과&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1624526579405&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class TemplatePatternDemo {
    public static void main(String[] args) {

        Game game = new Cricket();
        game.play();

        System.out.println();

        game = new Football();
        game.play();
    }
}&lt;/code&gt;&lt;/pre&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;reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://www.tutorialspoint.com/design_pattern/template_pattern.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.tutorialspoint.com/design_pattern/template_pattern.htm&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Design Pattern/행위 패턴</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/239</guid>
      <comments>https://wellbell.tistory.com/239#entry239comment</comments>
      <pubDate>Thu, 24 Jun 2021 18:24:55 +0900</pubDate>
    </item>
    <item>
      <title>Spring WebFlux + redis 간단 실습</title>
      <link>https://wellbell.tistory.com/238</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 포스팅에서 WebFlux의 코드, DB 등 동기, 블럭킹으로 작성되면 WebFlux의 장점을 잃고 MVC와 다를게 없는 성능을 보여준다고 말했었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는&amp;nbsp; redis-reactive를 통해서 WebFlux에서 redis를 사용하는 간단한 실습을 진행할려고합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습 코드는 &lt;a href=&quot;https://github.com/whdals7337/spring-webflux-study&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브 주소&lt;/a&gt;에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. redis-reactive, embedded redis 의존성 추가&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624483772439&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
	implementation 'it.ozimov:embedded-redis:0.7.2'
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. redis의 문자열의 key value를 사용하도록 Bean 등록&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624484192394&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class BasicRedisConfig {

    @Primary
    @Bean
    ReactiveRedisOperations&amp;lt;String, String&amp;gt; redisOperations(ReactiveRedisConnectionFactory factory) {
        RedisSerializer&amp;lt;String&amp;gt; serializer = new StringRedisSerializer();
        RedisSerializationContext&amp;lt;String, String&amp;gt; serializationContext = RedisSerializationContext
                .&amp;lt;String, String&amp;gt;newSerializationContext()
                .key(serializer)
                .value(serializer)
                .hashKey(serializer)
                .hashValue(serializer)
                .build();

        return new ReactiveRedisTemplate&amp;lt;&amp;gt;(factory, serializationContext);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. test 및 로컬 test를 위한 embedded redis 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624484175809&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import redis.embedded.RedisServer;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Configuration
public class EmbeddedRedisConfig {

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

    private RedisServer redisServer;

    @PostConstruct
    public void redisServer() {
        redisServer = new RedisServer(redisPort);
        redisServer.start();
    }

    @PreDestroy
    public void stopRedis() {
        redisServer.stop();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. handler 작성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- loadData(): 10만개의 데이터 로드 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- findReactorList(): 비동기, 논블로킹 방식의 메서드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- findNormalList(): 동기, 블로킹 방식의 메서드&lt;/p&gt;
&lt;pre id=&quot;code_1624483839275&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.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Component
@RequiredArgsConstructor
public class BasicHandler {

    private final ReactiveRedisConnectionFactory factory;
    private final ReactiveRedisOperations&amp;lt;String, String&amp;gt; reactiveRedisOperations;
    private final RedisTemplate&amp;lt;String , String&amp;gt; stringStringRedisTemplate;
    private static final AtomicInteger count = new AtomicInteger(0);

    public void loadData() {
        List&amp;lt;String&amp;gt; data = new ArrayList&amp;lt;&amp;gt;();
        IntStream.range(0, 100000).forEach(i -&amp;gt; data.add(UUID.randomUUID().toString()));

        Flux&amp;lt;String&amp;gt; stringFlux = Flux.fromIterable(data);

        factory.getReactiveConnection()
                .serverCommands()
                .flushAll()
                .thenMany(stringFlux.flatMap(uid -&amp;gt; reactiveRedisOperations.opsForValue()
                        .set(String.valueOf(count.getAndAdd(1)), uid)))
                .subscribe();
    }

    public Flux&amp;lt;String&amp;gt; findReactorList() {
        return reactiveRedisOperations
                .keys(&quot;*&quot;)
                .flatMap(key -&amp;gt; reactiveRedisOperations.opsForValue().get(key));
    }

    public Flux&amp;lt;String&amp;gt; findNormalList() {
        return Flux.fromIterable(Objects.requireNonNull(stringStringRedisTemplate.keys(&quot;*&quot;))
                .stream()
                .map(key -&amp;gt; stringStringRedisTemplate.opsForValue().get(key))
                .collect(Collectors.toList()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. router 등록&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 등록시 bean이름이 다른 router와 겹치지 않게 조심해야함. 동일 이름의 bean 2개 만들려고하면서 실패하게 됨&lt;/p&gt;
&lt;pre id=&quot;code_1624484073437&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.webflux.study.handler.BasicHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
@RequiredArgsConstructor
public class BasicRouter {

    private final BasicHandler basicHandler;

    @Bean
    public RouterFunction&amp;lt;ServerResponse&amp;gt; basicRoute() {
        return RouterFunctions.route()
                .GET(&quot;/reactive-list&quot;, serverRequest -&amp;gt; ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                        .body(basicHandler.findReactorList(), String.class))
                .GET(&quot;/normal-list&quot;, serverRequest -&amp;gt; ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                        .body(basicHandler.findNormalList(), String.class))
                .GET(&quot;/load&quot;, serverRequest -&amp;gt; { basicHandler.loadData(); return ServerResponse.ok()
                        .body(BodyInserters.fromValue(&quot;Load Data Completed&quot;)); })
                .build();
    }
}&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;이후 /load -&amp;gt; normal-list -&amp;gt; reactive-list 순으로 이동하면 WebFlux에 대해서 보다 이해 가능할 것으로 생각이 듭니다.&lt;/p&gt;</description>
      <category>Spring Webflux</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/238</guid>
      <comments>https://wellbell.tistory.com/238#entry238comment</comments>
      <pubDate>Thu, 24 Jun 2021 06:39:10 +0900</pubDate>
    </item>
    <item>
      <title>Spring Webflux 간단 개념 정리 및 실습</title>
      <link>https://wellbell.tistory.com/237</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring WebFlux는 Spring 5에서 새롭게 추가된 모듈입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebFlux는 클라이언트, 서버에서 reactive 스타일의 어플리케이션 개발을 도와주는 모듈이며,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reactive-stack web framework이며 non-blocking에 reactive stream을 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점 : 고성능, spring 과 완벽한 통합, netty 지원, 비동기&lt;span&gt;&amp;nbsp;&lt;/span&gt;non-blocking 메세지 처리, Back Pressure&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;netty&lt;/b&gt; : &lt;span style=&quot;color: #555555;&quot;&gt;프로토콜 서버 및 클라이언트와 같은 네트워크 응용 프로그램을 빠르고 쉽게 개발할 수있는&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;NIO(Non-Blocking Input Ouput)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;클라이언트 서버 프레임 워크&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;기존의 소켓 프로그래밍은 클라이언트가 접속하게 되면 스레드를 할당해야 하는데&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;(1:1&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;관계&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;),&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;정말 많은 클라이언트가 접속을 하게 될 경우 그 숫자만큼 스레드를 생성해야 해서 리소스의 낭비로 이루어지고&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;문맥 교환과 관련된 문제와 입력이나 출력 데이터에 관련한 무한 대기 현상이 발생하는 문제를 해결하기 위해서 등장&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;(상당히 방대한 내용이 있지만 주제에 벗어나는 듯하여 이는 다른 포스팅에서 정리하려고합니다.)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Back Pressure&lt;/b&gt; : 스프링 배치에서 다루었던 개념으로 빠른 Publisher - 느린 Subscriber 문제를 해결하는 원리이다. &lt;b&gt;Publisher의 일방적 데이터 Push 가 아니라, Subscriber가 처리할 수 있을 만큼의 데이터만&lt;/b&gt; Subscriber의 요청에 의해서 전달해주는 것이다.&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;1280&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE12EZ/btq7YpMXxyR/AAnreIGePIhZK4K9TdECf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE12EZ/btq7YpMXxyR/AAnreIGePIhZK4K9TdECf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE12EZ/btq7YpMXxyR/AAnreIGePIhZK4K9TdECf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE12EZ%2Fbtq7YpMXxyR%2FAAnreIGePIhZK4K9TdECf1%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;1280&quot; height=&quot;974&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;974&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;스프링 부트 2를 보면 Reactive Stack과 Servlet Stack이 있는데 주요하게 봐야하는 부분으로&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;1. Spring WebFlux vs Spring MVC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 MVC는 아마 스프링을 다뤄봤다 하면 경험이 있을탠데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 request : 1 thread&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sync + blocking&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이글에서 다루고자하는 Spring WebFlux는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;many request : 1 thread&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async + nonblock&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. DB&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebFlux로 개발하고 DB는 blocking 이라면 ? WebFlux를 쓸 이유가 없다. 라는 개념아래서 reactive를 지원하는 DB를 사용해야하는데 우리가 일반적으로 알고있는 RDBMS는 지원하지않고 Redis, Mongo 등은 지원한다. 실습에서는 Redis를 이용하는 부분을 다뤄 볼려고합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(R2DBC를 이용하면 MYSQL 등과 비동기 방식으로 연결이 가능한 듯합니다. )&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9XDBk/btq7Zfb2VMW/Wk2TRzAYoaAaex08eTOMd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9XDBk/btq7Zfb2VMW/Wk2TRzAYoaAaex08eTOMd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9XDBk/btq7Zfb2VMW/Wk2TRzAYoaAaex08eTOMd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9XDBk%2Fbtq7Zfb2VMW%2FWk2TRzAYoaAaex08eTOMd0%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;479&quot; height=&quot;313&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;중간 정리 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 그래서 WebFlux 쓰는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배민 기술 블로그를 보면 &lt;b&gt;가게노출 시스템&lt;/b&gt;에 WebFlux를 도입하였는데 내용을 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가게노출 시스템은 배달의민족 앱 내에서 가장 많은 트래픽을 소화하는 시스템으로, 다음과 같은 리소스 최적화가 반드시 필요&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 사용자 요청을 처리하기 위해 수십여개의 외부 시스템에 대한 요청이 필요한 시스템에서, 어떻게 가장 빠른 응답을 줄 수 있을까?&lt;/li&gt;
&lt;li&gt;수많은 요청을 처리해야할 때 어떻게 쓰레드 지옥을 벗어날 수 있을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서도 설명하였듯 Spring MVC는 1:1로 요청을 처리하기 때문에 트래픽이 몰리면 많은 쓰레드가 생겨납니다. 쓰레드가 전환될때 context switching 비용이 발생하게 되는데 쓰레드가 많을 수록이 비용이 커지게 되기 때문에 적절한 쓰레드의 수를 유지해야하는 문제가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 반해서 WebFlux&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Event-Driven&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Asynchronous Non-blocking I/O&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 통해 리소스를 효율적으로 사용할 수 있도록 만들어 줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cno0aX/btq72kDbp9g/6zhqftOpiMADNHe7bSkE5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cno0aX/btq72kDbp9g/6zhqftOpiMADNHe7bSkE5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cno0aX/btq72kDbp9g/6zhqftOpiMADNHe7bSkE5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcno0aX%2Fbtq72kDbp9g%2F6zhqftOpiMADNHe7bSkE5K%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;911&quot; height=&quot;430&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHHLHE/btq7V7ZYyrO/WrLIbsGEcXA06diylkHi20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHHLHE/btq7V7ZYyrO/WrLIbsGEcXA06diylkHi20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHHLHE/btq7V7ZYyrO/WrLIbsGEcXA06diylkHi20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHHLHE%2Fbtq7V7ZYyrO%2FWrLIbsGEcXA06diylkHi20%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;912&quot; height=&quot;339&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NSlsQ/btq7XrEgthT/NzJKgnemuZfLgJ5SBWDUD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NSlsQ/btq7XrEgthT/NzJKgnemuZfLgJ5SBWDUD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NSlsQ/btq7XrEgthT/NzJKgnemuZfLgJ5SBWDUD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNSlsQ%2Fbtq7XrEgthT%2FNzJKgnemuZfLgJ5SBWDUD0%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;908&quot; height=&quot;542&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://woowabros.github.io/experience/2020/02/19/introduce-shop-display.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://woowabros.github.io/experience/2020/02/19/introduce-shop-display.html&lt;/a&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;2. 이렇게 좋은데 왜 MVC를 쓰는 경우가 있나요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Webflux&lt;span&gt; 가 무조건 빠르고 좋은 것이 아니라 &lt;span style=&quot;color: #000000;&quot;&gt;Webflux&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;프로젝트의 비지니스 로직들이 모두 Async + NonBlocking 으로 되어있다면 빠를 수 있습니다. 그런데 이런 코드를 작성하는 것은 꾀... 어렵죠. ㅎ 아래 주소는 &lt;span style=&quot;background-color: #f9f9f9; color: #030303;&quot;&gt;NHN FORWARD 2020에서 WebFlux의 성능이 나오지 않는 부분에 대한 발표내용입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=I0zMm6wIbRI&quot;&gt;https://www.youtube.com/watch?v=I0zMm6wIbRI&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MVC는 못해도 본전이지만, Reative 는 못하면 MVC를 적용하느니만 못하다. 그리고 트래픽이 높은 서비스가 아니라면 MVC로도 감당 가능하다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Mono와 Flux&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Spring Webflux에서 사용하는 reactive library가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Reactor&lt;span&gt;이고 Reactor가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Reactive Streams&lt;span&gt;의 구현체입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Flux와 Mono는&lt;/span&gt;&amp;nbsp;&lt;span&gt;Reactor 객체&lt;/span&gt;이며,&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;차이점은 발행하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;데이터 갯수&lt;/b&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Flux : 0 ~ N 개의 데이터 전달&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Mono : 0 ~ 1 개의 데이터 전달&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;보통 여러 스트림을 하나의 결과를 모아줄 때 Mono를, 각각의 Mono를 합쳐서 하나의 여러 개의 값을 여러개의 값을 처리할 떄 Flux를 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런데&amp;nbsp;Flux도 0~1개의 데이터 전달이 가능한데,&amp;nbsp;굳이 한개까지만 데이터를 처리할 수 있는 Mono라는 타입이 필요할까요? 데이터 설계를 할때 결과가 없거나 하나의 결과값만 받는 것이 명백한 경우,&amp;nbsp; List나 배열을 사용하지 않는 것처럼, Multi Result가 아닌 하나의 결과셋만 받게 될 경우에는 불필요하게&amp;nbsp;Flux를 사용하지 않고&amp;nbsp;Mono를 사용하게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 의존성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624483318114&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id 'org.springframework.boot' version '2.5.1'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.webflux.study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'io.projectreactor:reactor-test'
}

test {
	useJUnitPlatform()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. handler 작성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624483346977&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import static org.springframework.web.reactive.function.BodyInserters.*;
import static org.springframework.web.reactive.function.server.ServerResponse.*;

@Component
public class HelloHandler {

    public Mono&amp;lt;ServerResponse&amp;gt; hello(ServerRequest request) {
        return ok().contentType(MediaType.TEXT_PLAIN)
                .body(fromValue(&quot;Hello &quot; + request.pathVariable(&quot;name&quot;)));
    }

    /*
    // 세미나 코드 (위와 동일한 내용)
    HandlerFunction helloHandler = req -&amp;gt;
            ok().body(fromValue(&quot;Hello&quot; + req.pathVariable(&quot;name&quot;))); // 세미나 코드에서 사용한 fromObject 가 @Deprecated -&amp;gt; fromValue
    */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. router 등록&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624483376491&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.webflux.study.handler.HelloHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.*;

@Configuration
@RequiredArgsConstructor
public class HelloRouter {

    private final HelloHandler helloHandler;

    @Bean
    public RouterFunction&amp;lt;ServerResponse&amp;gt; helloRoute() {
        return RouterFunctions
                .route(RequestPredicates.GET(&quot;/hello/{name}&quot;)
                        .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloHandler::hello);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 테스트 코드 작성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624483462754&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class HelloWebClientTest {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    public void testHello() {
        webTestClient
                // Create a GET request to test an endpoint
                .get().uri(&quot;/hello/jong&quot;)
                .accept(MediaType.TEXT_PLAIN)
                .exchange()
                // and use the dedicated DSL to test assertions against the response
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo(&quot;Hello jong&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring Webflux</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/237</guid>
      <comments>https://wellbell.tistory.com/237#entry237comment</comments>
      <pubDate>Thu, 24 Jun 2021 06:26:21 +0900</pubDate>
    </item>
    <item>
      <title>2. spring Batch Job Flow</title>
      <link>https://wellbell.tistory.com/236</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;이글을 창천 향로님의 블로그의 글을 따라한 내용입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/328?category=902551&quot;&gt;https://jojoldu.tistory.com/328?category=902551&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624192209522&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;4. Spring Batch 가이드 - Spring Batch Job Flow&quot; data-og-description=&quot;자 이번 시간부터 본격적으로 실전에서 사용할 수 있는 Spring Batch 내용들을 배워보겠습니다. 작업한 모든 코드는 Github에 있으니 참고하시면 됩니다. 앞서 Spring Batch의 Job을 구성하는데는 Step이 &quot; data-og-host=&quot;jojoldu.tistory.com&quot; data-og-source-url=&quot;https://jojoldu.tistory.com/328?category=902551&quot; data-og-url=&quot;https://jojoldu.tistory.com/328&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MDlzl/hyKCMbzVWZ/fudX4xJPppaygGKBG40lQ1/img.png?width=744&amp;amp;height=634&amp;amp;face=0_0_744_634,https://scrap.kakaocdn.net/dn/cps25H/hyKCH9bFmZ/NfonbD3ZIp1oFt9LDKmZV0/img.png?width=744&amp;amp;height=634&amp;amp;face=0_0_744_634,https://scrap.kakaocdn.net/dn/bydFQl/hyKD9XhOUf/lwrR7XlGV1rRbYWYypmG11/img.png?width=2392&amp;amp;height=1698&amp;amp;face=0_0_2392_1698&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/328?category=902551&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jojoldu.tistory.com/328?category=902551&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MDlzl/hyKCMbzVWZ/fudX4xJPppaygGKBG40lQ1/img.png?width=744&amp;amp;height=634&amp;amp;face=0_0_744_634,https://scrap.kakaocdn.net/dn/cps25H/hyKCH9bFmZ/NfonbD3ZIp1oFt9LDKmZV0/img.png?width=744&amp;amp;height=634&amp;amp;face=0_0_744_634,https://scrap.kakaocdn.net/dn/bydFQl/hyKD9XhOUf/lwrR7XlGV1rRbYWYypmG11/img.png?width=2392&amp;amp;height=1698&amp;amp;face=0_0_2392_1698');&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;4. Spring Batch 가이드 - Spring Batch Job Flow&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자 이번 시간부터 본격적으로 실전에서 사용할 수 있는 Spring Batch 내용들을 배워보겠습니다. 작업한 모든 코드는 Github에 있으니 참고하시면 됩니다. 앞서 Spring Batch의 Job을 구성하는데는 Step이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jojoldu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;앞서 Spring Batch의 Job을 구성하는데는 Step 존재하는데 &lt;span style=&quot;color: #555555;&quot;&gt;Step은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;실제 Batch 작업을 수행하는 역할을 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;- &lt;span style=&quot;color: #555555;&quot;&gt;Step에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Batch로 실제 처리하고자 하는 기능과 설정을 모두 포함&lt;span style=&quot;color: #555555;&quot;&gt;하는 장소&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;1. Next&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next()&lt;span style=&quot;color: #555555;&quot;&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;순차적으로 Step들 연결시킬때 사용&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;step1 -&amp;gt; step2 -&amp;gt; stpe3 순으로 하나씩 실행시킬때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;next()&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;는 좋은 방법&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624191377056&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Configuration
@RequiredArgsConstructor
public class StepNextJobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job stepNextJob() {
        return jobBuilderFactory.get(&quot;stepNextJob&quot;)
                .start(step1())
                .next(step2())
                .next(step3())
                .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get(&quot;step1&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step1&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get(&quot;step2&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step2&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step step3() {
        return stepBuilderFactory.get(&quot;step3&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step3&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml에 추가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Spring Batch가 실행될때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Program arguments로&lt;span&gt;&amp;nbsp;&lt;/span&gt;job.name&lt;span&gt;&amp;nbsp;&lt;/span&gt;값이 넘어오면 해당 값과 일치하는 Job만 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624191555104&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.batch.job.names: ${job.name:NONE}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${job.name:NONE}&lt;span style=&quot;color: #555555;&quot;&gt;을 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;:&lt;span style=&quot;color: #555555;&quot;&gt;를 사이에 두고 좌측에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;job.name&lt;span style=&quot;color: #555555;&quot;&gt;이, 우측에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;NONE&lt;span style=&quot;color: #555555;&quot;&gt;이 있는데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;이 코드의 의미는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;job.name&lt;b&gt;이 있으면&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;job.name&lt;b&gt;값을 할당하고, 없으면&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;NONE&lt;b&gt;을 할당&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;하겠다는 의미&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt; - NONE&lt;span style=&quot;color: #555555;&quot;&gt;이 할당되면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;어떤 배치도 실행하지 않겠다는 의미&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;523&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfLFas/btq7COmqZlG/jGTLMOi4QFdK1QIUQwq7pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfLFas/btq7COmqZlG/jGTLMOi4QFdK1QIUQwq7pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfLFas/btq7COmqZlG/jGTLMOi4QFdK1QIUQwq7pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfLFas%2Fbtq7COmqZlG%2FjGTLMOi4QFdK1QIUQwq7pk%2Fimg.png&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;523&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;228&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zVFeK/btq7FFav85Y/YbwdQDSI27JCiJHhw8oU30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zVFeK/btq7FFav85Y/YbwdQDSI27JCiJHhw8oU30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zVFeK/btq7FFav85Y/YbwdQDSI27JCiJHhw8oU30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzVFeK%2Fbtq7FFav85Y%2FYbwdQDSI27JCiJHhw8oU30%2Fimg.png&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;228&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;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;span style=&quot;color: #555555;&quot;&gt;Next의 경우 &lt;/span&gt;&lt;b&gt;앞의 step에서 오류가 나면 나머지 뒤에 있는 step 들은 실행되지 못하게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;하지만 상황에 따라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;정상일때는 Step B로, 오류가 났을때는 Step C로 수행해야할때&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;이럴 경우를 대비해 Spring Batch Job에서는 조건별로 Step을 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;711&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIzhLs/btq7Ezvn1pI/FyxVfFI3RCKb1xVSzPOliK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIzhLs/btq7Ezvn1pI/FyxVfFI3RCKb1xVSzPOliK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIzhLs/btq7Ezvn1pI/FyxVfFI3RCKb1xVSzPOliK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIzhLs%2Fbtq7Ezvn1pI%2FFyxVfFI3RCKb1xVSzPOliK%2Fimg.png&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;711&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;pre id=&quot;code_1624192055074&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Configuration
@RequiredArgsConstructor
public class StepNextConditionalJobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job stepNextConditionalJob() {
        return jobBuilderFactory.get(&quot;stepNextConditionalJob&quot;)
                .start(conditionalJobStep1())
                    .on(&quot;FAILED&quot;) // FAILED 일 경우
                    .to(conditionalJobStep3()) // step3으로 이동한다.
                    .on(&quot;*&quot;) // step3의 결과 관계 없이 
                    .end() // step3으로 이동하면 Flow가 종료한다.
                .from(conditionalJobStep1()) // step1로부터
                    .on(&quot;*&quot;) // FAILED 외에 모든 경우
                    .to(conditionalJobStep2()) // step2로 이동한다.
                    .next(conditionalJobStep3()) // step2가 정상 종료되면 step3으로 이동한다.
                    .on(&quot;*&quot;) // step3의 결과 관계 없이 
                    .end() // step3으로 이동하면 Flow가 종료한다.
                .end() // Job 종료
                .build();
    }

    @Bean
    public Step conditionalJobStep1() {
        return stepBuilderFactory.get(&quot;step1&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is stepNextConditionalJob Step1&quot;);

                    /**
                        ExitStatus를 FAILED로 지정한다.
                        해당 status를 보고 flow가 진행된다.
                    **/
                    contribution.setExitStatus(ExitStatus.FAILED);

                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step conditionalJobStep2() {
        return stepBuilderFactory.get(&quot;conditionalJobStep2&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is stepNextConditionalJob Step2&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step conditionalJobStep3() {
        return stepBuilderFactory.get(&quot;conditionalJobStep3&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is stepNextConditionalJob Step3&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}&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;위의 코드 시나리오는 step1이 실패하냐 성공하냐에 따라 시나리오가 달라지는데요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;step1 실패 시나리오: step1 -&amp;gt; step3&lt;/li&gt;
&lt;li&gt;step1 성공 시나리오: step1 -&amp;gt; step2 -&amp;gt; step3&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.on()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐치할&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ExitStatus&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;지정&lt;/li&gt;
&lt;li&gt;*&lt;span&gt;&amp;nbsp;&lt;/span&gt;일 경우 모든 ExitStatus가 지정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;to()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음으로 이동할 Step 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;from()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일종의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;이벤트 리스너&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;역할&lt;/li&gt;
&lt;li&gt;상태값을 보고 일치하는 상태라면&lt;span&gt;&amp;nbsp;&lt;/span&gt;to()에 포함된&lt;span&gt;&amp;nbsp;&lt;/span&gt;step을 호출합니다.&lt;/li&gt;
&lt;li&gt;step1의 이벤트 캐치가 FAILED로 되있는 상태에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;추가로 이벤트 캐치&lt;/b&gt;하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;from을 써야만 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;end()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;end는 FlowBuilder를 반환하는 end와 FlowBuilder를 종료하는 end 2개가 있음&lt;/li&gt;
&lt;li&gt;on(&quot;*&quot;)뒤에 있는 end는 FlowBuilder를 반환하는 end&lt;/li&gt;
&lt;li&gt;build()&lt;span&gt;&amp;nbsp;&lt;/span&gt;앞에 있는 end는 FlowBuilder를 종료하는 end&lt;/li&gt;
&lt;li&gt;FlowBuilder를 반환하는 end 사용시 계속해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;from을 이어갈 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;on&lt;span style=&quot;color: #555555;&quot;&gt;이 캐치하는 상태값이 BatchStatus가 아닌 ExitStatus라는 점 &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;- &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;분기처리를 위해 상태값 조정이 필요하시다면 ExitStatus를 조정해야함&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Batch Status vs. Exit Status&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;BatchStatus는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Job 또는 Step 의 실행 결과를 Spring에서 기록할 때 사용&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;하는 Enum&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;- &lt;span style=&quot;color: #555555;&quot;&gt;BatchStatus로 사용 되는 값은 COMPLETED, STARTING, STARTED, STOPPING, STOPPED, FAILED, ABANDONED, UNKNOWN&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;ExitStatus는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Step의 실행 후 상태&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Spring Batch는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;기본적으로 ExitStatus의 exitCode는 Step의 BatchStatus와 같도록&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정이 되어 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;하지만 만약에 본인만의 커스텀한 exitCode가 필요하다면 &lt;span style=&quot;color: #555555;&quot;&gt;exitCode를 반환하는 별도의 로직이 필요&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624192436368&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.start(step1())
    .on(&quot;FAILED&quot;)
    .end()
.from(step1())
    .on(&quot;COMPLETED WITH SKIPS&quot;)
    .to(errorPrint1())
    .end()
.from(step1())
    .on(&quot;*&quot;)
    .to(step2())
    .end()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tep1이 실패하며, Job 또한 실패하게 된다.&lt;/li&gt;
&lt;li&gt;step1이 성공적으로 수행되어 step2가 수행된다.&lt;/li&gt;
&lt;li&gt;step1이 성공적으로 완료되며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;COMPLETED WITH SKIPS의 exit 코드로 종료 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;COMPLETED WITH SKIPS&lt;span style=&quot;color: #555555;&quot;&gt;는 ExitStatus에는 없는 코드 - &lt;span style=&quot;color: #555555;&quot;&gt;하는대로 처리되기 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;COMPLETED WITH SKIPS&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;exitCode를 반환하는 별도의 로직이 필요&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624192487395&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SkipCheckingListener extends StepExecutionListenerSupport {

    public ExitStatus afterStep(StepExecution stepExecution) {
        String exitCode = stepExecution.getExitStatus().getExitCode();
        if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &amp;amp;&amp;amp; 
              stepExecution.getSkipCount() &amp;gt; 0) {
            return new ExitStatus(&quot;COMPLETED WITH SKIPS&quot;);
        }
        else {
            return null;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Step이 성공적으로 수행되었는지 확인하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;StepExecution의 skip 횟수가 0보다 클 경우&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;COMPLETED WITH SKIPS&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;의 exitCode를 갖는 ExitStatus를 반환&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;4. Decide&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 진행했던 방식은 2가지 문제가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Step이 담당하는 역할이 2개 이상이 됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 해당 Step이 처리해야할 로직외에도 분기처리를 시키기 위해 ExitStatus 조작이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다양한 분기 로직 처리의 어려움
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ExitStatus를 커스텀하게 고치기 위해선 Listener를 생성하고 Job Flow에 등록하는 등 번거로움이 존재합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;JobExecutionDecider&lt;span&gt; : &lt;/span&gt;&lt;/span&gt;Spring Batch에서는 Step들의 Flow속에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&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;pre id=&quot;code_1624192707414&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Configuration
@RequiredArgsConstructor
public class DeciderJobConfiguration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job deciderJob() {
        return jobBuilderFactory.get(&quot;deciderJob&quot;)
                .start(startStep())
                .next(decider()) // 홀수 | 짝수 구분
                .from(decider()) // decider의 상태가
                .on(&quot;ODD&quot;) // ODD라면
                .to(oddStep()) // oddStep로 간다.
                .from(decider()) // decider의 상태가
                .on(&quot;EVEN&quot;) // ODD라면
                .to(evenStep()) // evenStep로 간다.
                .end() // builder 종료
                .build();
    }

    @Bean
    public Step startStep() {
        return stepBuilderFactory.get(&quot;startStep&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Start!&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step evenStep() {
        return stepBuilderFactory.get(&quot;evenStep&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 짝수입니다.&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step oddStep() {
        return stepBuilderFactory.get(&quot;oddStep&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 홀수입니다.&quot;);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public JobExecutionDecider decider() {
        return new OddDecider();
    }

    public static class OddDecider implements JobExecutionDecider {

        @Override
        public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
            Random rand = new Random();

            int randomNumber = rand.nextInt(50) + 1;
            log.info(&quot;랜덤숫자: {}&quot;, randomNumber);

            if(randomNumber % 2 == 0) {
                return new FlowExecutionStatus(&quot;EVEN&quot;);
            } else {
                return new FlowExecutionStatus(&quot;ODD&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flow: startStep -&amp;gt; oddDecider에서 홀수 인지 짝수인지 구분 -&amp;gt; oddStep or evenStep 진행&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;start()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Job Flow의 첫번째 Step을 시작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;next()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;startStep&lt;span&gt;&amp;nbsp;&lt;/span&gt;이후에&lt;span&gt;&amp;nbsp;&lt;/span&gt;decider를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;from()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;4-2와 마찬가지로 from은 이벤트 리스너 역할을 합니다.&lt;/li&gt;
&lt;li&gt;decider의 상태값을 보고 일치하는 상태라면&lt;span&gt;&amp;nbsp;&lt;/span&gt;to()에 포함된&lt;span&gt;&amp;nbsp;&lt;/span&gt;step&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 호출합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;분기 로직에 대한 모든 일은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;OddDecider&lt;span style=&quot;color: #555555;&quot;&gt;가 전담 - &lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Step과는 명확히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;역할과 책임이 분리&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;된채 진행&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/328?category=902551&quot;&gt;https://jojoldu.tistory.com/328?category=902551&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Batch</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/236</guid>
      <comments>https://wellbell.tistory.com/236#entry236comment</comments>
      <pubDate>Sun, 20 Jun 2021 21:40:31 +0900</pubDate>
    </item>
    <item>
      <title>1. 기본 개념과  메타 데이터 테이블</title>
      <link>https://wellbell.tistory.com/235</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;배치 어플리케이션&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대용량 데이터 - 배치 어플리케이션은 대량의 데이터를 가져오거나, 전달하거나, 계산하는 등의 처리를 할 수 ​​있어야 합니다.&lt;/li&gt;
&lt;li&gt;자동화 - 배치 어플리케이션은 심각한 문제 해결을 제외하고는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;사용자 개입 없이 실행&lt;/b&gt;되어야 합니다.&lt;/li&gt;
&lt;li&gt;견고성 - 배치 어플리케이션은 잘못된 데이터를 충돌/중단 없이 처리할 수 있어야 합니다.&lt;/li&gt;
&lt;li&gt;신뢰성 - 배치 어플리케이션은 무엇이 잘못되었는지를 추적할 수 있어야 합니다. (로깅, 알림)&lt;/li&gt;
&lt;li&gt;성능 - 배치 어플리케이션은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지정한 시간 안에 처리를 완료&lt;/b&gt;하거나 동시에 실행되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 어플리케이션을 방해하지 않도록 수행&lt;/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;&lt;span style=&quot;color: #555555;&quot;&gt;Accenture의 배치 노하우 &amp;amp; 기술력과 Spring 프레임워크가 합쳐져 만들어진 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DI, AOP, 서비스 추상화&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;등 Spring 프레임워크의 3대 요소를 모두 사용할 수 있으면서, Accenture의 Batch 노하우가 담긴 아키텍처를 사용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Quartz는 스케줄러의 역할&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;이지, Batch 와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;대용량 데이터 배치 처리에 대한 기능을 지원하지 않습니다&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;반대로 Batch 역시 Quartz의 다양한 스케줄 기능을 지원하지 않아서 보통은 Quartz + Batch를 조합해서 사용합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;정해진 스케줄마다 Quartz가 Spring Batch를 실행하는 구조라고 보시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정해진 시간마다 데이터 가공이 필요&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;한 경우에 어디서든 Spring Batch가 사용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;479&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnbf0N/btq7L0ybJvj/iMUxIYyxZk5ZMxKPKSclmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnbf0N/btq7L0ybJvj/iMUxIYyxZk5ZMxKPKSclmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnbf0N/btq7L0ybJvj/iMUxIYyxZk5ZMxKPKSclmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbnbf0N%2Fbtq7L0ybJvj%2FiMUxIYyxZk5ZMxKPKSclmK%2Fimg.png&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;479&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Batch Job을 생성하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;simpleJob 코드를 보시면 simpleStep1을 품고 있음&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;을 알 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Spring Batch에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Job은 하나의 배치 작업 단위&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;를 얘기하는데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Job 안에는 아래처럼 여러 Step이 존재하고, Step 안에 Tasklet 혹은 Reader &amp;amp; Processor &amp;amp; Writer 묶음이 존재합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;br /&gt;&lt;b&gt;Tasklet 하나와 Reader &amp;amp; Processor &amp;amp; Writer 한 묶음이 같은 레벨&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Reader &amp;amp; Processor가 끝나고 Tasklet으로 마무리 짓는 등으로 만들순 없다습니다.&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;591&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mR8b0/btq7L1DRSoY/a86yGGoVOBvmG4vEMpUTs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mR8b0/btq7L1DRSoY/a86yGGoVOBvmG4vEMpUTs1/img.png&quot; data-alt=&quot;출처:&amp;amp;amp;nbsp; metaDataSchema&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mR8b0/btq7L1DRSoY/a86yGGoVOBvmG4vEMpUTs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmR8b0%2Fbtq7L1DRSoY%2Fa86yGGoVOBvmG4vEMpUTs1%2Fimg.png&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;591&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:&amp;nbsp; metaDataSchema&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Spring Batch에선 메타 데이터 테이블들이 필요&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Batch의 메타 데이터는 다음과 같은 내용들을 담고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전에 실행한 Job이 어떤 것들이 있는지&lt;/li&gt;
&lt;li&gt;최근 실패한 Batch Parameter가 어떤것들이 있고, 성공한 Job은 어떤것들이 있는지&lt;/li&gt;
&lt;li&gt;다시 실행한다면 어디서 부터 시작하면 될지&lt;/li&gt;
&lt;li&gt;어떤 Job에 어떤 Step들이 있었고, Step들 중 성공한 Step과 실패한 Step들은 어떤것들이 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;기본적으로 H2 DB를 사용할 경우엔 해당 테이블을 Boot가 실행될때 자동으로 생성해주지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;MySQL이나 Oracle과 같은 DB를 사용할때는 개발자가 직접 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA6FvQ/btq7JSNVHnI/OK8qZLoOq7dr6kU6qYPt60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA6FvQ/btq7JSNVHnI/OK8qZLoOq7dr6kU6qYPt60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA6FvQ/btq7JSNVHnI/OK8qZLoOq7dr6kU6qYPt60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA6FvQ%2Fbtq7JSNVHnI%2FOK8qZLoOq7dr6kU6qYPt60%2Fimg.png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;267&quot; data-origin-height=&quot;468&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yI69P/btq7DVyQK95/DanoKc6THTj2lJsQIKCyb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yI69P/btq7DVyQK95/DanoKc6THTj2lJsQIKCyb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yI69P/btq7DVyQK95/DanoKc6THTj2lJsQIKCyb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyI69P%2Fbtq7DVyQK95%2FDanoKc6THTj2lJsQIKCyb0%2Fimg.png&quot; data-origin-width=&quot;267&quot; data-origin-height=&quot;468&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;247&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mPTb3/btq7EzPCJuh/WPpc4PF71Eer9ahMxgyoZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mPTb3/btq7EzPCJuh/WPpc4PF71Eer9ahMxgyoZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mPTb3/btq7EzPCJuh/WPpc4PF71Eer9ahMxgyoZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmPTb3%2Fbtq7EzPCJuh%2FWPpc4PF71Eer9ahMxgyoZ0%2Fimg.png&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;247&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SimpleJobConfiguration&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624189328498&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Configuration
public class SimpleJobConfiguration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job simpleJob() {
        return jobBuilderFactory.get(&quot;simpleJob&quot;)
                .start(simpleStep1())
                .build();
    }

    @Bean
    public Step simpleStep1() {
        return stepBuilderFactory.get(&quot;simpleStep1&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt; This is Step1&quot;);
                    return RepeatStatus.FINISHED;
                }).build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BATCH_JOB_INSTANCE&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Job Parameter에 따라 생성되는 테이블로 실행했던 simpleJob을 확인 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Job Parameter - &lt;b&gt;Spring Batch가 실행될때 외부에서 받을 수 있는 파라미터&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;92&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWKH2S/btq7I0MecbD/3x745QRzSSv13miMziIvrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWKH2S/btq7I0MecbD/3x745QRzSSv13miMziIvrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWKH2S/btq7I0MecbD/3x745QRzSSv13miMziIvrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWKH2S%2Fbtq7I0MecbD%2F3x745QRzSSv13miMziIvrK%2Fimg.png&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;92&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JOB_INSTANCE_ID
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BATCH_JOB_INSTANCE&lt;span&gt;&amp;nbsp;&lt;/span&gt;테이블의 PK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JOB_NAME
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수행한 Batch Job Name&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1624189353938&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Configuration
public class SimpleJobConfiguration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job simpleJob() {
        return jobBuilderFactory.get(&quot;simpleJob&quot;)
                .start(simpleStep1(null))
                .build();
    }

    @Bean
    @JobScope
    public Step simpleStep1(@Value(&quot;#{jobParameters[requestDate]}&quot;) String requestDate) {
        return stepBuilderFactory.get(&quot;simpleStep1&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step1&quot;);
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; requestDate = {}&quot;, requestDate);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;525&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sx1yS/btq7EA139ve/o6eHMwpWfPwiLf10JxC8k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sx1yS/btq7EA139ve/o6eHMwpWfPwiLf10JxC8k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sx1yS/btq7EA139ve/o6eHMwpWfPwiLf10JxC8k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSx1yS%2Fbtq7EA139ve%2Fo6eHMwpWfPwiLf10JxC8k0%2Fimg.png&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;525&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;122&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1ZsSq/btq7FFVRmTc/ZKRhdJ4mD0ZMjSJqzkUUIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1ZsSq/btq7FFVRmTc/ZKRhdJ4mD0ZMjSJqzkUUIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1ZsSq/btq7FFVRmTc/ZKRhdJ4mD0ZMjSJqzkUUIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1ZsSq%2Fbtq7FFVRmTc%2FZKRhdJ4mD0ZMjSJqzkUUIk%2Fimg.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;122&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;반복 실행시&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624189476922&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Caused by: org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={requestDate=20180805}.  If you want to run this job again, change the parameters.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;requestDate 변경 후 실행시&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;519&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bghpwW/btq7FF2FSX2/ZvCmxrgG5LXJIFpzkEAVl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bghpwW/btq7FF2FSX2/ZvCmxrgG5LXJIFpzkEAVl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bghpwW/btq7FF2FSX2/ZvCmxrgG5LXJIFpzkEAVl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbghpwW%2Fbtq7FF2FSX2%2FZvCmxrgG5LXJIFpzkEAVl1%2Fimg.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;519&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;76&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzbMRY/btq7JRhbi8a/NcwBT6rObFwxKIcl1NefR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzbMRY/btq7JRhbi8a/NcwBT6rObFwxKIcl1NefR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzbMRY/btq7JRhbi8a/NcwBT6rObFwxKIcl1NefR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzbMRY%2Fbtq7JRhbi8a%2FNcwBT6rObFwxKIcl1NefR1%2Fimg.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;76&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;145&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwrfva/btq7Dnhzi2o/1L9HxtzHq6gekEzIZGfs00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwrfva/btq7Dnhzi2o/1L9HxtzHq6gekEzIZGfs00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwrfva/btq7Dnhzi2o/1L9HxtzHq6gekEzIZGfs00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwrfva%2Fbtq7Dnhzi2o%2F1L9HxtzHq6gekEzIZGfs00%2Fimg.png&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;145&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;h4 id=&quot;3-2-batch_job_execution&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BATCH_JOB_EXECUTION&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JOB_EXECUTION와 JOB_INSTANCE는 부모-자식 관계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;JOB_EXECUTION은 자신의 부모&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;JOB_INSTACNE가 성공/실패했던 모든 내역을 갖고 있습니다&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;JOB_EXECUTION 테이블을 보시면 3개의 ROW가 있는데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;저희가 실행했던 파라미터가 없는 simpleJob,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;requestDate=20180805&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파라미터로 실행했던 simpleJob,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;requestDate=20180806&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파라미터로 실행했던 simpleJob 까지 이렇게 3개의 실행 데이터&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;161&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BhxWR/btq7DsjqyNs/qAG4FZnItUh7OmFn4W8rBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BhxWR/btq7DsjqyNs/qAG4FZnItUh7OmFn4W8rBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BhxWR/btq7DsjqyNs/qAG4FZnItUh7OmFn4W8rBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBhxWR%2Fbtq7DsjqyNs%2FqAG4FZnItUh7OmFn4W8rBK%2Fimg.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;161&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;중간에 실패 시켰을 경우&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624189873371&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Configuration
public class SimpleJobConfiguration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job simpleJob() {
        return jobBuilderFactory.get(&quot;simpleJob&quot;)
                .start(simpleStep1(null))
                .next(simpleStep2(null))
                .build();
    }

    @Bean
    @JobScope
    public Step simpleStep1(@Value(&quot;#{jobParameters[requestDate]}&quot;) String requestDate) {
        return stepBuilderFactory.get(&quot;simpleStep1&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    throw new IllegalArgumentException(&quot;step1에서 실패합니다.&quot;);
                })
                .build();
    }

    @Bean
    @JobScope
    public Step simpleStep2(@Value(&quot;#{jobParameters[requestDate]}&quot;) String requestDate) {
        return stepBuilderFactory.get(&quot;simpleStep2&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step2&quot;);
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; requestDate = {}&quot;, requestDate);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;253&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ENPbd/btq7DsRfWu5/ndBzhGVKZ45JZtuXEiLlY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ENPbd/btq7DsRfWu5/ndBzhGVKZ45JZtuXEiLlY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ENPbd/btq7DsRfWu5/ndBzhGVKZ45JZtuXEiLlY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FENPbd%2Fbtq7DsRfWu5%2FndBzhGVKZ45JZtuXEiLlY1%2Fimg.png&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;253&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;180&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0m5BE/btq7DrrhSgU/MWPRvc3nqJ9lXX5XaFz7c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0m5BE/btq7DrrhSgU/MWPRvc3nqJ9lXX5XaFz7c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0m5BE/btq7DrrhSgU/MWPRvc3nqJ9lXX5XaFz7c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0m5BE%2Fbtq7DrrhSgU%2FMWPRvc3nqJ9lXX5XaFz7c0%2Fimg.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;180&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;547&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yEmy8/btq7KlijqEo/vZfzVT48H8zb3TpQeJBkIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yEmy8/btq7KlijqEo/vZfzVT48H8zb3TpQeJBkIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yEmy8/btq7KlijqEo/vZfzVT48H8zb3TpQeJBkIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyEmy8%2Fbtq7KlijqEo%2FvZfzVT48H8zb3TpQeJBkIK%2Fimg.png&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;547&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중간 실패 로직을 정상으로 변경 후 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624190057787&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RequiredArgsConstructor
@Configuration
public class SimpleJobConfiguration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job simpleJob() {
        return jobBuilderFactory.get(&quot;simpleJob&quot;)
                .start(simpleStep1(null))
                .next(simpleStep2(null))
                .build();
    }

    @Bean
    @JobScope
    public Step simpleStep1(@Value(&quot;#{jobParameters[requestDate]}&quot;) String requestDate) {
        return stepBuilderFactory.get(&quot;simpleStep1&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    //throw new IllegalArgumentException(&quot;step1에서 실패합니다.&quot;);
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step1&quot;);
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; requestDate = {}&quot;, requestDate);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    @JobScope
    public Step simpleStep2(@Value(&quot;#{jobParameters[requestDate]}&quot;) String requestDate) {
        return stepBuilderFactory.get(&quot;simpleStep2&quot;)
                .tasklet((contribution, chunkContext) -&amp;gt; {
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; This is Step2&quot;);
                    log.info(&quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; requestDate = {}&quot;, requestDate);
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}&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;동일한 JOB_INSTANCE_ID 값에 대한 실패와 성공 데이터가 쌓이는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;552&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wljBd/btq7JSNWJkq/FMO0BO0kJ5CmDSVi66tM21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wljBd/btq7JSNWJkq/FMO0BO0kJ5CmDSVi66tM21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wljBd/btq7JSNWJkq/FMO0BO0kJ5CmDSVi66tM21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwljBd%2Fbtq7JSNWJkq%2FFMO0BO0kJ5CmDSVi66tM21%2Fimg.png&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;552&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Spring Batch는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;동일한 Job Parameter로 성공한 기록이 있을때만 재수행이 안된다&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;는 것을 알 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Reference&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/326&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jojoldu.tistory.com/326&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/325&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jojoldu.tistory.com/325&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/324&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jojoldu.tistory.com/324&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Spring Batch</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/235</guid>
      <comments>https://wellbell.tistory.com/235#entry235comment</comments>
      <pubDate>Sun, 20 Jun 2021 21:05:01 +0900</pubDate>
    </item>
    <item>
      <title>6. Spring Cloud Config</title>
      <link>https://wellbell.tistory.com/234</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Config server&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;435&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edioAI/btq7I051AMv/RkRGVrL62aH48HRDVrYI21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edioAI/btq7I051AMv/RkRGVrL62aH48HRDVrYI21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edioAI/btq7I051AMv/RkRGVrL62aH48HRDVrYI21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedioAI%2Fbtq7I051AMv%2FRkRGVrL62aH48HRDVrYI21%2Fimg.png&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;435&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마이크로서비스의 어떠한 설정(환경변수값, Spring cloud 설정 등)이 변경되었을때 서버 재시작 없이 동적으로 적용&lt;/li&gt;
&lt;li&gt;마이크로서비스가 배포될때 제반 설정값들을 배포 대상 환경(개발계, 검증계, 운영계 등)에 맞게 적용&lt;/li&gt;
&lt;li&gt;마이크로서비스를 Stateless하게 개발하기 위해서입니다. Stateless하게 만들어야 스케일링(마이크로서비스 인스턴스 서버 - 즉, 컨테이너의 증감)과 부담없는 재시작이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 의존성 및 config repository 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1624113509760&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.cloud:spring-cloud-config-server')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;482&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv6XeA/btq7C3DB97l/lW16wqqXuc8lKrWwNs0XA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv6XeA/btq7C3DB97l/lW16wqqXuc8lKrWwNs0XA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv6XeA/btq7C3DB97l/lW16wqqXuc8lKrWwNs0XA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv6XeA%2Fbtq7C3DB97l%2FlW16wqqXuc8lKrWwNs0XA1%2Fimg.png&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;482&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;pre id=&quot;code_1624113483711&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/whdals7337/spring-cloud-workshop-config-repository&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1624113472716&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Spring Boot Actuator 의존성 및 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html&quot;&gt;&lt;b&gt;Spring Boot Actuator&lt;/b&gt;&lt;/a&gt;란 Spring Boot 기반의 애플리케이션을 손쉽게 모니터링 할수 있는 데이터를 제공해주는 라이브러리이다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹의 상태 모니터링과 metric,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;traffic&lt;/span&gt;정보 그리고 database의 상태등을 알 수 있다. &lt;/span&gt;이러한 모니터링 데이터를 직접 구현할 필요없이 간단한 설정으로 사용할 수 있다는 것이 큰 장점&lt;/p&gt;
&lt;pre id=&quot;code_1624113657988&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
    compile('org.springframework.cloud:spring-cloud-starter-config') // config 의존성 추가
    compile('org.springframework.boot:spring-boot-starter-actuator') // starter-actuator 의존성 추가
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;spring boot 1.x 때는 추가적으로 무언가 해줄 필요는 없었지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;2.x 부터는 default로 대부분의 정보를 노출하지 않는 것으로 되어 있기 때문에&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;해당 부분을 변경해줘야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래 설정에서는 refresh만 허용하였지만 *를 통해서 전체 허용도 가능하며 exclude를 통해서 특정 endpoint를 제외한 나머지를 적용할수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624113545541&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;management:
  endpoints:
    web:
      exposure:
        include: refresh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;bootstrap.yml&lt;span&gt;&amp;nbsp;&lt;/span&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;application.yml&amp;nbsp;&lt;/b&gt;전에 로드하게 되어 spring cloud configserver를 사용하는 경우 사용되는데 이유는 기본적으로 외부 소스에서 구성 속성을 로드하고 로컬 외부 구성 파일의 속성을 해독 하기 위해서 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1624113584270&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# bootstrap.yml
server:
  port: 8082

spring:
  application:
    name: product
  cloud:
    config:
      uri: http://127.0.0.1:8080&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/234</guid>
      <comments>https://wellbell.tistory.com/234#entry234comment</comments>
      <pubDate>Sun, 20 Jun 2021 00:03:02 +0900</pubDate>
    </item>
    <item>
      <title>5. zuul - [Spring Cloud를 활용한 MSA 기초 온라인 강의 실습]</title>
      <link>https://wellbell.tistory.com/233</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Zuul - API Gateway&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;765&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PVDeK/btq7Ch2VANc/UZI4prYZBIeX2TZJknKOIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PVDeK/btq7Ch2VANc/UZI4prYZBIeX2TZJknKOIk/img.png&quot; data-alt=&quot;출처 - https://github.com/Netflix/zuul/wiki&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PVDeK/btq7Ch2VANc/UZI4prYZBIeX2TZJknKOIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPVDeK%2Fbtq7Ch2VANc%2FUZI4prYZBIeX2TZJknKOIk%2Fimg.png&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;765&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - https://github.com/Netflix/zuul/wiki&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API Gateway는 API의 요청자인 Client와 API의 제공자인 backend service를 연결하는 중계자(또는 출입문)&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;API Gateway가 필요한 이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 횡단 관심사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인증/인가: 부적절한 요청을 차단하여 Backend service를 보호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rate limiting: 일정량 이상의 요청 제한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- metering: 계측&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Logging: 유통되는 트래픽 로깅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- proxy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. L/B &amp;amp; Routing: Client의 요청에 따라 적절한 backend service를 로드밸런싱(L/B: Load Balancing)하고 연결(라우팅)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Circuit Break: Backend service 장애 감지 및 대처&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;1000&quot; data-origin-height=&quot;779&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTiIYP/btq7BeyEdan/OCzfG0T8XSWoMy8vT3rD2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTiIYP/btq7BeyEdan/OCzfG0T8XSWoMy8vT3rD2K/img.png&quot; data-alt=&quot;출처 - https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTiIYP/btq7BeyEdan/OCzfG0T8XSWoMy8vT3rD2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTiIYP%2Fbtq7BeyEdan%2FOCzfG0T8XSWoMy8vT3rD2K%2Fimg.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;779&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zuul Filter는 크게 4가지 Filter로 나누어 진다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PRE Filter - 라우팅전에 실행되며 필터이다. 주로 logging, 인증등이 pre Filter에서 이루어 진다.&lt;/li&gt;
&lt;li&gt;ROUTING Filter - 요청에 대한 라우팅을 다루는 필터이다. Apache httpclient를 사용하여 정해진 Url로 보낼수 있고, Neflix Ribbon을 사용하여 동적으로 라우팅 할 수도 있다.&lt;/li&gt;
&lt;li&gt;POST Filter - 라우팅 후에 실행되는 필터이다. response에 HTTP header를 추가하거나, response에 대한 응답속도, Status Code, 등 응답에 대한 statistics and metrics을 수집한다.&lt;/li&gt;
&lt;li&gt;ERROR Filter - 에러 발생시 실행되는 필터이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;4-zuul-components&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Zuul Components&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zuul은 zuul-core, zuul-simple-webapp, zuul-netflix, zuul-netflix-webapp 4개의 컨포넌트로 구성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;zuul-core : 위에서 설명한 Zuul의 Request Lifecycle를 담당하고, Fliter를 컴파일하고 실행한는 기능을 담당하고 있는 Zuul의 core library&lt;/li&gt;
&lt;li&gt;zuul-netflix : 기본적은 Zuul에 NetflixOSS library를 추가한다.&lt;/li&gt;
&lt;li&gt;zuul-simple-webapp : zuul-core만 사용한 아주 기본적은 web application&lt;/li&gt;
&lt;li&gt;zuul-netflix-webapp : zuul-core와 zuul-netflix를 함께 사용한 web application&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API GATEWAY를 구축하기 위해서는 zuul-simple-webapp를 사용하다는 것은 MSA 환경에서 아주 유용한 NetflixOSS library를 포기하는 것이다. zuul-netflix-webapp을 도입하기에는 학습곡선이 크다. 그래서 찾은게 spring cloud netflix 프로젝트이다.&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;892&quot; data-origin-height=&quot;692&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vEkfi/btq7Edx1tCY/AXsdkUrSkt0hyvHxD7QKo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vEkfi/btq7Edx1tCY/AXsdkUrSkt0hyvHxD7QKo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vEkfi/btq7Edx1tCY/AXsdkUrSkt0hyvHxD7QKo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvEkfi%2Fbtq7Edx1tCY%2FAXsdkUrSkt0hyvHxD7QKo0%2Fimg.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;692&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RouteLocator은 url path에 대한 routing을 지정한다. 요청이 들어오면 url path로 어떻게 routing 할 것인가를 정의하고 있다.&lt;/li&gt;
&lt;li&gt;ZuulHandlerMapping은 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping을 상속받고, RouteLocator에 정의된 url path에 zuulController를 매핑한다. RouteLocator에 정의된 path의 요청이 들어오면 zuulController를 호출하게 된다.&lt;/li&gt;
&lt;li&gt;ZuulController은 org.springframework.web.servlet.mvc.ServletWrappingController를 상속받으며, ZuulServlet을 주입시킨다. 그래서 ZuulController로 들어온 모든 요청은 ZuulServlet으로 처리한다.&lt;/li&gt;
&lt;li&gt;ZuulFilterInitializer는 filter Map에 정의된 filter를 FilterRegistry에 등록하고, FilterLoader로 로딩한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Cloud Zuul은 @EnableZuulProxy와 @EnableZuulServer 두 종류의 annotation으로 Zuul을 구동시킨다. 두개는 완전히 다른 것이 아니고 @EnableZuulProxy가 @EnableZuulServer을 포함한다. @EnableZuulServer에서 PreDecorationFilter, RibbonRoutingFilter, SimpleHostRoutingFilter를 추가하면, @EnableZuulProxy가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212121;&quot;&gt;더 자세한 설명은 &lt;a href=&quot;http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html&quot;&gt;http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 의존성 추가&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624020560514&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-netflix-zuul')
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. zuul, eureka 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624020589588&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  application:
    name: zuul

server:
  port: 8765

zuul:
  routes:
    product:
      path: /products/**
      serviceId: product
      stripPrefix: false
    display:
      path: /displays/**
      serviceId: display
      stripPrefix: false
  ribbon-isolation-strategy: thread
  thread-pool:
    use-separate-thread-pools: true
    thread-pool-key-prefix: zuul-

eureka:
  instance:
    non-secure-port: ${server.port}
    prefer-ip-address: true
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. config&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624020576853&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. zuul에 hystrix, ribbon 설정해서 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624020749060&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;compile('org.springframework.retry:spring-retry:1.2.2.RELEASE')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1624020654260&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000
    product:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000
  threadpool:
    zuul-product:
      coreSize: 30
      maximumSize: 100
      allowMaximumSizeToDivergeFromCoreSize: true
    zuul-display:
      coreSize: 30
      maximumSize: 100
      allowMaximumSizeToDivergeFromCoreSize: true

product:
  ribbon:
    MaxAutoRetriesNextServer: 1
    ReadTimeout: 3000
    ConnectTimeout: 1000
    MaxTotalConnections: 300
    MaxConnectionsPerHost: 100

display:
  ribbon:
    MaxAutoRetriesNextServer: 1
    ReadTimeout: 3000
    ConnectTimeout: 1000
    MaxTotalConnections: 300
    MaxConnectionsPerHost: 100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reference&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=6g1wH97BiuQ&amp;amp;list=PL9mhQYIlKEhdtYdxxZ6hZeb0va2Gm17A5&amp;amp;index=8&quot;&gt;https://www.youtube.com/watch?v=6g1wH97BiuQ&amp;amp;list=PL9mhQYIlKEhdtYdxxZ6hZeb0va2Gm17A5&amp;amp;index=8&lt;/a&gt; &lt;a href=&quot;https://woowabros.github.io/r&amp;amp;d/2017/06/13/apigateway.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://woowabros.github.io/r&amp;amp;d/2017/06/13/apigateway.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/233</guid>
      <comments>https://wellbell.tistory.com/233#entry233comment</comments>
      <pubDate>Fri, 18 Jun 2021 21:58:38 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 다트 게임 문제 (자바)</title>
      <link>https://wellbell.tistory.com/232</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/17682&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/17682&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624019524219&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;코딩테스트 연습 - [1차] 다트 게임&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/17682&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17682&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8uh2K/hyKBAhDhaa/8ilpiV0detwqEHyPZVd870/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bv2qNF/hyKBMPUYBT/GUjGZTvQMKvNoP3QuawHy1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/EYGvA/hyKBH17gV7/Q0mtYrkCttCBlnqhdFJnxK/img.png?width=300&amp;amp;height=400&amp;amp;face=116_216_207_315&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/17682&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17682&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8uh2K/hyKBAhDhaa/8ilpiV0detwqEHyPZVd870/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bv2qNF/hyKBMPUYBT/GUjGZTvQMKvNoP3QuawHy1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/EYGvA/hyKBH17gV7/Q0mtYrkCttCBlnqhdFJnxK/img.png?width=300&amp;amp;height=400&amp;amp;face=116_216_207_315');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - [1차] 다트 게임&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;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굉장히 단순하게 풀이한 문제로 리팩토링을 통해서 조금더 코드를 다듬을 수 있을거 같지만 일단 글을 쓸려고 합니다.ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 숫자면 stack에 넣고 문자일 경우 문자별 처리를 다르게 가져가서 처리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자가 10까지 있어서 10인 경우 check가 필요하다는 점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타상(*)은 첫 번째 기회에서도 나올 수 있다. 이 경우 첫 번째 스타상(*)의 점수만 2배가 된다. 즉 stack에 값이 하나 밖에 없는데 스타상이 나온 경우를 신경 써야 한다는 점&lt;/p&gt;
&lt;pre id=&quot;code_1624019505875&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Stack;

// 프로그래머스 다트 게임 문제
class Solution {
    public int solution(String dartResult) {
        Stack&amp;lt;Integer&amp;gt; stack = new Stack&amp;lt;&amp;gt;();
        for (int i = 0; i &amp;lt; dartResult.length(); i++) {
            char c = dartResult.charAt(i);
            if (c == 'S') {
                continue;
            }

            if (c == 'D') {
                stack.add((int) Math.pow(stack.pop(), 2));
                continue;
            }

            if (c == 'T') {
                stack.add((int) Math.pow(stack.pop(), 3));
                continue;
            }

            if (c == '*') {
                int now = stack.pop();
                if (!stack.isEmpty()) {
                    stack.add(stack.pop() * 2);
                }
                stack.add(now * 2);
                continue;
            }

            if (c == '#') {
                stack.add(stack.pop() * -1);
                continue;
            }

            if (c - '0' == 1 &amp;amp;&amp;amp; dartResult.charAt(i + 1) == '0') {
                stack.add(10);
                i++;
            }else {
                stack.add(c - '0');
            }
        }

        int answer = 0;
        for (int score : stack) {
            answer += score;
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/232</guid>
      <comments>https://wellbell.tistory.com/232#entry232comment</comments>
      <pubDate>Fri, 18 Jun 2021 21:36:13 +0900</pubDate>
    </item>
    <item>
      <title>4. Feign - [Spring Cloud를 활용한 MSA 기초 온라인 강의 실습]</title>
      <link>https://wellbell.tistory.com/231</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Interface 선언을 통해 자동으로 Http Client를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 RestTemplate은 concreate 클래스라 테스트가 어려움&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;- 관심x : 어떤 URL, 어떻게 파싱할 것인가&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;a href=&quot;https://woowabros.github.io/experience/2019/05/29/feign.html&quot;&gt;https://woowabros.github.io/experience/2019/05/29/feign.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#_creating_feign_clients_manually&quot;&gt;https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#_creating_feign_clients_manually&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 의존성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935242525&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-openfeign')  // To use Feign
    ...
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935340092&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class DisplayApplication {

    public static void main(String[] args) {
        SpringApplication.run(DisplayApplication.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;&lt;b&gt;3. FeignClient 인터페이스 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935413849&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @FeignClient(name = &quot;product&quot;, url = &quot;http://localhost:8082/&quot;) // 유레카 미 사용시
@FeignClient(name = &quot;product&quot;)
public interface FeignProductRemoteService {
    @RequestMapping(path = &quot;/products/{productId}&quot;)
    String getProductInfo(@PathVariable(&quot;productId&quot;) String productId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 인터페이스 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935447105&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(path = &quot;/displays&quot;)
public class DisplayController {

    private final ProductRemoteService productRemoteService;
    private final FeignProductRemoteService feignProductRemoteService;

    public DisplayController(ProductRemoteService productRemoteService,
                             FeignProductRemoteService feignProductRemoteService) {
        this.productRemoteService = productRemoteService;
        this.feignProductRemoteService = feignProductRemoteService;
    }

    @GetMapping(path = &quot;/{displayId}&quot;)
    public String getDisplayDetail(@PathVariable String displayId) {
        String productInfo = getProductInfo();
        return String.format(&quot;[display id = %s at %s %s ]&quot;, displayId, System.currentTimeMillis(), productInfo);
    }

    private String getProductInfo() {
        return feignProductRemoteService.getProductInfo(&quot;12345&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. hystrix 설정을 - Feign 의 모든 요청에 hystrix가 적용됨&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935756928&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;feign:
  hystrix:
    enabled: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6-1. fallback&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935778460&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;product&quot;, fallback = FeignProductRemoteServiceFallbackImpl.class)
public interface FeignProductRemoteService {
    @RequestMapping(path = &quot;/products/{productId}&quot;)
    String getProductInfo(@PathVariable(&quot;productId&quot;) String productId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1623935793235&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class FeignProductRemoteServiceFallbackImpl implements FeignProductRemoteService {
    @Override
    public String getProductInfo(String productId) {
        return &quot;[ this product is sold out ]&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6-2. FallbackFactory&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623935832360&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class FeignProductRemoteServiceFallbackFactory implements FallbackFactory&amp;lt;FeignProductRemoteService&amp;gt; {

    @Override
    public FeignProductRemoteService create(Throwable cause) {
        System.out.println(&quot;t = &quot; + cause);
        return productId -&amp;gt; &quot;[ this product is sold out ]&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1623935820959&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;product&quot;, fallbackFactory = FeignProductRemoteServiceFallbackFactory.class)
public interface FeignProductRemoteService {
    @RequestMapping(path = &quot;/products/{productId}&quot;)
    String getProductInfo(@PathVariable(&quot;productId&quot;) String productId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7. Feign의 hystrix 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623936235922&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hystrix:
  command:
    FeignProductRemoteService#getProductInfo(String):
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000   # default 1,000ms
      circuitBreaker:
        requestVolumeThreshold: 1   # Minimum number of request to calculate circuit breaker's health. default 20
        errorThresholdPercentage: 50 # Error percentage to open circuit. default 50&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/231</guid>
      <comments>https://wellbell.tistory.com/231#entry231comment</comments>
      <pubDate>Thu, 17 Jun 2021 22:27:37 +0900</pubDate>
    </item>
    <item>
      <title>3. Eureka - [Spring Cloud를 활용한 MSA 기초 온라인 강의 실습]</title>
      <link>https://wellbell.tistory.com/230</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;410&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsg5pq/btq7utABBEM/GbwDKGrYhKDPG0BCgkznF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsg5pq/btq7utABBEM/GbwDKGrYhKDPG0BCgkznF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsg5pq/btq7utABBEM/GbwDKGrYhKDPG0BCgkznF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsg5pq%2Fbtq7utABBEM%2FGbwDKGrYhKDPG0BCgkznF0%2Fimg.png&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;410&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;앞선 리본 예제에서 서버 목록을 yml에 직접 넣었는데 이를 자동화 할 방법으로 유레카를 사용하게된다.&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;유레카 서버는 registry로 유레카에 등록된 서비스들을 관리한다 보시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유레카 클라이언트는 각각의 서버 혹은 인스턴스의 시작시 유레카 서버에 자동으로 자신의 상태를 등록(설정으로 컨트롤 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기적 heartBeat를 통해서 유레카 서버에 자신이 살아 있음을 알리게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;21-서버-측-설정&quot; data-ke-size=&quot;size23&quot;&gt;서버 측 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.server.enable-self-preservation&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일시적인 네트워크 장애로 인한 서비스 해제 막기 위한 자기 보호 모드 (디폴트 true, 운영에선 반드시 true 로 설정 필요)&lt;/li&gt;
&lt;li&gt;원래는 해당 시간안에 하트비트가 일정 횟수 이상 들어오지 않아야 서비스 해제하는데 false 설정 시 하트비트가 들어오지 않으면 바로 서비스 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.server.response-cache-update-interval-ms&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eureka Server 의 캐싱 업데이트 주기 (디폴트 30,000ms)&lt;/li&gt;
&lt;li&gt;Eureka Server 실행 후 /eureka/apps API 실행 시 아무것도 나오지 않음 클라이언트 인스턴스 실행 수 /eureka/apps 실행 시 여전히 아무것도 나오지 않음 30초가 지나고 /eureka/apps 실행 시 클라이언트 인스턴스 조회됨.&lt;/li&gt;
&lt;li&gt;Eureka Server 의 대시보드(유레카서버:8761/) 에 등록된 인스턴스가 표시될때에는 캐시 사용하지 않음&lt;/li&gt;
&lt;li&gt;eureka.client.registry-fetch-interval-seconds&lt;span&gt;&amp;nbsp;&lt;/span&gt;와 비교하여 볼 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.server.eviction-interval-timer-in-ms&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트로부터 하트비트가 계속 수신 되는지 점검 (디폴트 60초)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;31-서버클라이언트-측-모두-설정&quot; data-ke-size=&quot;size23&quot;&gt;서버/클라이언트 측 모두 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.client.register-with-eureka&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레지스트리에 자신을 등록할지에 대한 여부 (디폴트 true)&lt;/li&gt;
&lt;li&gt;클러스터링 모드의 Eureka Server구성은 서로 peering 구성이 가능. (Eureka Server 설정에 정의된 peering 노드를 찾아서 레지스트리 정보의 sync 를 맞춤)&lt;/li&gt;
&lt;li&gt;독립 실행형 모드(standalone)에서는 peering 실패가 발생하므로 Eureka Client측 동작을 끔&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.client.fetch-registry&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레지스트리에 있는 정보를 가져올지에 대한 여부 (디폴트 true)&lt;/li&gt;
&lt;li&gt;true 로 설정 시 검색할 때마다 Eureka Server 를 호출하는 대신 레지스트리가 로컬로 캐싱됨&lt;/li&gt;
&lt;li&gt;30초마다 Eureka Client 가 유레카 레지스트리 변경 사항 여부 재확인함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;41-클라이언트-측-설정&quot; data-ke-size=&quot;size23&quot;&gt;클라이언트 측 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.client.registry-fetch-interval-seconds&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스 목록을 설정한 시간마다 캐싱 (디폴트 30초)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.client.disable-delta&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐싱 시 변경된 부분만 업데이트할 지 여부 (디폴트 false)&lt;/li&gt;
&lt;li&gt;false 로 설정 서 대역폭 낭비이므로 true 로 설정할 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.instance.lease-renewal-interval-in-seconds&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eureka Server 로 설정된 시간(second)마다 하트비트 전송 (디폴트 30초)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eureka.instance.lease-expiration-duration-in-seconds&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디스커버리는 서비스 등록 해제 하기 전에 마지막 하트비트에서부터 설정된 시간(second) 동안 하트비트가 수신되지 않으면 서비스 등록 해제 (디폴트 90초)&lt;/li&gt;
&lt;li&gt;단, 유레카 인스턴스가 정상적으로 종료된 경우는 레지스트리에서 바로 제거&lt;/li&gt;
&lt;li&gt;이 값은&lt;span&gt;&amp;nbsp;&lt;/span&gt;lease-renewal-interval-in-seconds&lt;span&gt;&amp;nbsp;&lt;/span&gt;보다 커야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등 여러 설정이 더 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;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;a href=&quot;https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-eureka-server.html&quot;&gt;https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-eureka-server.html&lt;/a&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1623837226152&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Two Peer Aware Eureka Servers
---
spring:
  profiles: peer1
eureka:
  instance:
    hostname: peer1
  client:
    serviceUrl:
      defaultZone: http://peer2/eureka/

---
spring:
  profiles: peer2
eureka:
  instance:
    hostname: peer2
  client:
    serviceUrl:
      defaultZone: http://peer1/eureka/
      
      
      
 ---------------------------------------------------------
 
 # Three Peer Aware Eureka Servers
 eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/

---
spring:
  profiles: peer1
eureka:
  instance:
    hostname: peer1

---
spring:
  profiles: peer2
eureka:
  instance:
    hostname: peer2

---
spring:
  profiles: peer3
eureka:
  instance:
    hostname: peer3&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&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;1. 유레카 서버 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트에 유레카 서버 의존성을 추가하고 @EnableEurekaServer을 통해서 auto config되면서 유레카 서버가 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1623835125754&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1623835098504&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&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;b&gt;1) 유레카 클라이언트 의존성 추가&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623835187312&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') // To use Eureka client
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 유레카 서버의 주소를 defaltzone으로 설정하여 서버와 연결한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623835227340&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka  # default address&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1623835208545&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableEurekaClient
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 기존 리본 실습에서 설정햇던 &lt;span style=&quot;color: #000000;&quot;&gt;listOfServers설정을 제거한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623835248041&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;product:
  ribbon:
    MaxAutoRetries: 0
    MaxAutoRetriesNextServer: 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;유레카에 정상적으로 등록된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1813&quot; data-origin-height=&quot;962&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sAjkm/btq7smvYjCj/elX3KoURncm9QyWKopWwp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sAjkm/btq7smvYjCj/elX3KoURncm9QyWKopWwp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sAjkm/btq7smvYjCj/elX3KoURncm9QyWKopWwp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsAjkm%2Fbtq7smvYjCj%2FelX3KoURncm9QyWKopWwp0%2Fimg.png&quot; data-origin-width=&quot;1813&quot; data-origin-height=&quot;962&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=iIqamVxYmUk&amp;amp;list=PL9mhQYIlKEhdtYdxxZ6hZeb0va2Gm17A5&amp;amp;index=6&quot;&gt;https://www.youtube.com/watch?v=iIqamVxYmUk&amp;amp;list=PL9mhQYIlKEhdtYdxxZ6hZeb0va2Gm17A5&amp;amp;index=6&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/230</guid>
      <comments>https://wellbell.tistory.com/230#entry230comment</comments>
      <pubDate>Wed, 16 Jun 2021 18:57:28 +0900</pubDate>
    </item>
    <item>
      <title>2. Ribbon - [Spring Cloud를 활용한 MSA 기초 온라인 강의 실습]</title>
      <link>https://wellbell.tistory.com/229</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;254&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRmcLC/btq7nQ4rVvf/5s9i7LjDTdmfuckqbZP080/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRmcLC/btq7nQ4rVvf/5s9i7LjDTdmfuckqbZP080/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRmcLC/btq7nQ4rVvf/5s9i7LjDTdmfuckqbZP080/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRmcLC%2Fbtq7nQ4rVvf%2F5s9i7LjDTdmfuckqbZP080%2Fimg.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;254&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리본을 설명하기 앞서 로드 밸런싱에 대해서 이해하고 가겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 어플리케이션이 다른 어플리케이션을 호출할 때 대상 어플리케이션 인스턴스가 하나밖에 없다면 어떻게 될까요 ?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 대상앱 인스턴스가 죽거나 HTTP hang이 걸리면 서비스에 장애가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 보통 어플리케이션 인스턴스는 1개가 아니라 최소 2개 이상을 띄웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 인스턴스에 요청을 나눠주는 친구가 로드밸런서, 요청을 나눠주는 행위를 로드 밸런싱이라고 합니다.&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;Client LoadBalancer - Ribbon&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;271&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXCRUY/btq7ntPoQ0c/vfWdakLFkY3o9Y2J86xyyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXCRUY/btq7ntPoQ0c/vfWdakLFkY3o9Y2J86xyyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXCRUY/btq7ntPoQ0c/vfWdakLFkY3o9Y2J86xyyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXCRUY%2Fbtq7ntPoQ0c%2FvfWdakLFkY3o9Y2J86xyyK%2Fimg.png&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;271&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ribbon은 Load balancing을 요청 어플리케이션 단에서 수행해주는&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Client-side Load balancer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ribbon과 같은 L/B가 필요한 이유는&amp;nbsp;&lt;b&gt;부하 분산을 적절하게 하여 서비스의 가용성을 최대화하기 위함&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API Gateway도 Ribbon을 통해 Backend service를 로드밸런싱합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ribbon을 사용하면 API Gateway없이 대상 어플리케이션을 직접 로드밸런싱하여 연결할 수도 있습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습은 hytrix가 적용된 프로젝트에 ribbon을 추가한 부분으로 향후 유레카를 적용하면 보다 더 편하게 사용할 수 있는 부분은 다음 유레카 포스팅에서 다루도록 하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 의존성&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1623767385200&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.retry:spring-retry:1.2.2.RELEASE')  // spring cloud requires spring-retry for auto-retry
    compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon')  // 2. To use ribbon
    compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') // 1. To use spring-cloud-hystrix
    ...
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. &lt;span style=&quot;color: #000000;&quot;&gt;yml을 이용한 설정&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리본의 각종 설정&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가적인 설정의 아래 공식 사이트 참조&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623767532866&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;product: # 서비스 이름
  ribbon: # 리본 등록
    listOfServers: localhost:8082,localhost:7777 # 해당 서비스 URI 목록
    MaxAutoRetries: 0 # 같은 서버에 재시도 횟수
    MaxAutoRetriesNextServer: 1 # 재시도를 시도할 서버 개수&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 기본 설정 적용&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;restTemplate에 @LoadBalanced 어노테이션으로 리본 listOfServers로 등록된 서버로 로드 밸런싱을 진행&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623767408398&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableCircuitBreaker
public class DisplayApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(DisplayApplication.class);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 리본 사용&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 url을 적는 곳에 서비스 이름으로 대체하면 끝&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623767561148&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ProductRemoteServiceImpl implements ProductRemoteService {

    private static final String url = &quot;http://product/products/&quot;; // 서비스 이름으로 대체
    private final RestTemplate restTemplate;

    public ProductRemoteServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    @HystrixCommand(commandKey = &quot;productInfo&quot;, fallbackMethod = &quot;getProductInfoFallback&quot;)
    public String getProductInfo(String productId) {
        return this.restTemplate.getForObject(url + productId, String.class);
    }

    public String getProductInfoFallback(String productId, Throwable t) {
        System.out.println(&quot;t = &quot; + t);
        return &quot;[ this product is sold out ]&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=K4laj5cDYLg&quot;&gt;https://www.youtube.com/watch?v=K4laj5cDYLg&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://happycloud-lee.tistory.com/214&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://happycloud-lee.tistory.com/214&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/229</guid>
      <comments>https://wellbell.tistory.com/229#entry229comment</comments>
      <pubDate>Tue, 15 Jun 2021 23:26:59 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 110 옮기기 문제 (자바)</title>
      <link>https://wellbell.tistory.com/228</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/77886&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/77886&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623766448513&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;코딩테스트 연습 - 110 옮기기&quot; data-og-description=&quot;0과 1로 이루어진 어떤 문자열 x에 대해서, 당신은 다음과 같은 행동을 통해 x를 최대한 사전 순으로 앞에 오도록 만들고자 합니다. x에 있는 &amp;quot;110&amp;quot;을 뽑아서, 임의의 위치에 다시 삽입합니다. 예를 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/77886&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/77886&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xhjiZ/hyKAhBimcf/UdPeFR7JySX0veHZbR7EL1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/p414u/hyKAnBv2gY/ET1yZWfNZsXj5pFQOqSRj0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/vZWdl/hyKyXEsA2v/s0YnI4yuFQgdtXw3I5KAs0/img.png?width=1097&amp;amp;height=600&amp;amp;face=0_0_1097_600&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/77886&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/77886&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xhjiZ/hyKAhBimcf/UdPeFR7JySX0veHZbR7EL1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/p414u/hyKAnBv2gY/ET1yZWfNZsXj5pFQOqSRj0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/vZWdl/hyKyXEsA2v/s0YnI4yuFQgdtXw3I5KAs0/img.png?width=1097&amp;amp;height=600&amp;amp;face=0_0_1097_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 110 옮기기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;0과 1로 이루어진 어떤 문자열 x에 대해서, 당신은 다음과 같은 행동을 통해 x를 최대한 사전 순으로 앞에 오도록 만들고자 합니다. x에 있는 &quot;110&quot;을 뽑아서, 임의의 위치에 다시 삽입합니다. 예를&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;무슨 알고리즘을 사용해야하는 거지라는 생각과 고민 끝에 다른 사람으 코드를 보고 이해한 문제로&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. 문자에서 110이 나오는 경우 카운트를 세고 stack에는 담지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. stack에서 0이 나오기 직전 위치를 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 0 직전 위치를 기준으로 110을 카운트한 개수 만큼 넣어준다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;0111111010&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; stack 1110, cnt = 2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; idx = 1, sb = 0111&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; sb = 0110111 cnt = 1 idx = 4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; sb = 0110110111 cnt = 0; idx = 7&lt;/p&gt;
&lt;pre id=&quot;code_1623766461067&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Stack;

// 프로그래머스 110 옮기기 문제
class Solution {
    public String[] solution(String[] s) {
        String[] answer = new String[s.length];
        StringBuilder sb;

        for(int i=0; i&amp;lt;s.length; i++) {
            String str = s[i];
            Stack&amp;lt;Character&amp;gt; stack = new Stack&amp;lt;&amp;gt;();
            int cnt = 0;

            for(int j=0; j&amp;lt;str.length(); j++) {
                char z = str.charAt(j);

                if(stack.size()&amp;gt;=2) {
                    char y = stack.pop();
                    char x = stack.pop();

                    if(x=='1' &amp;amp;&amp;amp; y=='1' &amp;amp;&amp;amp; z=='0') {
                        cnt++;
                    } else {
                        stack.push(x);
                        stack.push(y);
                        stack.push(z);
                    }
                } else {
                    stack.push(z);
                }
            }

            int idx = stack.size();
            boolean flag = false;
            sb = new StringBuilder();
            while(!stack.isEmpty()) {
                if(!flag &amp;amp;&amp;amp; stack.peek()=='1') {
                    idx--;
                }

                if(!flag &amp;amp;&amp;amp; stack.peek()=='0') {
                    flag = true;
                }

                sb.insert(0, stack.pop());
            }

            if(cnt&amp;gt;0) {
                while(cnt&amp;gt;0) {
                    sb.insert(idx, &quot;110&quot;);
                    idx += 3;
                    cnt--;
                }
                answer[i] = sb.toString();
            } else {
                answer[i] = s[i];
            }
        }
        return answer;
    }
}&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;Reference&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@pss407/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A477886-110-%EC%98%AE%EA%B8%B0%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@pss407/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A477886-110-%EC%98%AE%EA%B8%B0%EA%B8%B0&lt;/a&gt;&lt;/p&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/228</guid>
      <comments>https://wellbell.tistory.com/228#entry228comment</comments>
      <pubDate>Tue, 15 Jun 2021 23:25:17 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 숫자 게임 문제 (자바)</title>
      <link>https://wellbell.tistory.com/227</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12987&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623657297725&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;xx 회사의 2xN명의 사원들은 N명씩 두 팀으로 나눠 숫자 게임을 하려고 합니다. 두 개의 팀을 각각 A팀과 B팀이라고 하겠습니다. 숫자 게임의 규칙은 다음과 같습니다. 먼저 모든 사원이 무작위로 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SXbAX/hyKy5g6cfs/ib7wTvz4kJOEDAlKkmACF1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bFM7Il/hyKy3wNGti/zO0Szw9xv7tSfu7dX6HxlK/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/12987&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SXbAX/hyKy5g6cfs/ib7wTvz4kJOEDAlKkmACF1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bFM7Il/hyKy3wNGti/zO0Szw9xv7tSfu7dX6HxlK/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;xx 회사의 2xN명의 사원들은 N명씩 두 팀으로 나눠 숫자 게임을 하려고 합니다. 두 개의 팀을 각각 A팀과 B팀이라고 하겠습니다. 숫자 게임의 규칙은 다음과 같습니다. 먼저 모든 사원이 무작위로&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;왜 3단계에 있지 하는 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A의 각 값들을 최소한의 차이로 이기도록 B의 사람을 내보내고 숫자를 세면 끝나는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A [5,1,3,7], B [2,2,6,8]&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;A[1, 3, 5, 7], B[2, 2, 6, 8]&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;B의 값이 큰 경우 점수 1점 획득 및 다음 A 와 비교하도록 aIndex++; 하면 끝이다.&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;ps&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;pre id=&quot;code_1623657285452&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Arrays;

// 프로그래머스 숫자 게임 문제
class NumberGame {
    public int solution(int[] A, int[] B) {
        Arrays.sort(A);
        Arrays.sort(B);

        int aIndex = 0;
        int bIndex = 0;
        int answer = 0;
        while (aIndex != A.length &amp;amp;&amp;amp; bIndex != B.length) {
            if (A[aIndex] &amp;lt; B[bIndex]) {
                answer++;
                aIndex++;
            }
            bIndex++;
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/227</guid>
      <comments>https://wellbell.tistory.com/227#entry227comment</comments>
      <pubDate>Mon, 14 Jun 2021 17:00:41 +0900</pubDate>
    </item>
    <item>
      <title>1. hystrix - [Spring Cloud를 활용한 MSA 기초 온라인 강의 실습]</title>
      <link>https://wellbell.tistory.com/226</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;Netflix에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://martinfowler.com/bliki/CircuitBreaker.html&quot;&gt;Circuit Breaker Pattern&lt;/a&gt;&lt;span style=&quot;color: #555555;&quot;&gt;을 구현한 라이브러리이다. Micro Service Architecture에서 장애 전파 방지를 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로 서비스 아키텍처에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;분산된 서비스간 통신이 원활하지 않은 경우&lt;/u&gt;에 각 서비스가&amp;nbsp;&lt;b&gt;&lt;span&gt;장애 내성&lt;/span&gt;&lt;/b&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;지연 내성&lt;/span&gt;&lt;/b&gt;을 갖게하도록 도와주는 라이브러리다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;882&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t6Ova/btq7d5mAqx4/b5D7NNIyg9JgKzKvL79hN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t6Ova/btq7d5mAqx4/b5D7NNIyg9JgKzKvL79hN1/img.png&quot; data-alt=&quot;출처:&amp;amp;amp;nbsp;https://supawer0728.github.io/2018/03/11/Spring-Cloud-Hystrix/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t6Ova/btq7d5mAqx4/b5D7NNIyg9JgKzKvL79hN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft6Ova%2Fbtq7d5mAqx4%2Fb5D7NNIyg9JgKzKvL79hN1%2Fimg.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;882&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:&amp;nbsp;https://supawer0728.github.io/2018/03/11/Spring-Cloud-Hystrix/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;장애-연쇄&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;장애 연쇄&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;495&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1cZI/btq666AeTOy/k2Dhs0KKBNRQKiz1PYDltK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1cZI/btq666AeTOy/k2Dhs0KKBNRQKiz1PYDltK/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://cloud.spring.io/spring-cloud-netflix/multi/multi__circuit_breaker_hystrix_clients.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1cZI/btq666AeTOy/k2Dhs0KKBNRQKiz1PYDltK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF1cZI%2Fbtq666AeTOy%2Fk2Dhs0KKBNRQKiz1PYDltK%2Fimg.png&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;495&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://cloud.spring.io/spring-cloud-netflix/multi/multi__circuit_breaker_hystrix_clients.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;supplier 서버에 장애가 생겨 항상 Timeout이 발생하는 경우,&lt;span&gt;&amp;nbsp;&lt;/span&gt;supplier 서버를 호출한&lt;span&gt;&amp;nbsp;&lt;/span&gt;client 서버는 Timeout이 발생할 때까지 응답이 밀리게 된다. 응답이 밀리는 동안 요청이 계속 쌓여 결국&lt;span&gt;&amp;nbsp;&lt;/span&gt;client 서버까지 요청이 과하게 밀려 장애가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 상황이 발생하지 않도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;circuit breaker를 두어 장애 전파를 막을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Hystrix-Flow-Chart&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Hystrix Flow Chart&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;667&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLdSxL/btq7bDKFzmL/Xzfrs5h5SU8jmshYwqgdDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLdSxL/btq7bDKFzmL/Xzfrs5h5SU8jmshYwqgdDk/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://github.com/Netflix/Hystrix/wiki/How-it-Works&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLdSxL/btq7bDKFzmL/Xzfrs5h5SU8jmshYwqgdDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLdSxL%2Fbtq7bDKFzmL%2FXzfrs5h5SU8jmshYwqgdDk%2Fimg.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;667&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://github.com/Netflix/Hystrix/wiki/How-it-Works&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HystrixCommand,&lt;span&gt;&amp;nbsp;&lt;/span&gt;HystrixObservableCommand&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체 생성 - &lt;span data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJsaXN0LXVub3JkZXJlZCUyMiUyQyUyMmlzVm9pZCUyMiUzQWZhbHNlJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCUyQyUyMm5vZGVzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIyYmxvY2slMjIlMkMlMjJ0eXBlJTIyJTNBJTIybGlzdC1pdGVtJTIyJTJDJTIyaXNWb2lkJTIyJTNBZmFsc2UlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQiU4QiVBOCVFQyU5RCVCQyUyMEFQSSVFQyU5NyU5MCUyMCVFQiU4QyU4MCVFRCU5NSU5OCVFQyU5NyVBQyUyMEh5c3RyaXglRUIlQTUlQkMlMjAlRUMlQTAlODElRUMlOUElQTklMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RA==&quot;&gt;&amp;nbsp;Hystrix 명령 적용&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJsaXN0LXVub3JkZXJlZCUyMiUyQyUyMmlzVm9pZCUyMiUzQWZhbHNlJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCUyQyUyMm5vZGVzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIyYmxvY2slMjIlMkMlMjJ0eXBlJTIyJTNBJTIybGlzdC1pdGVtJTIyJTJDJTIyaXNWb2lkJTIyJTNBZmFsc2UlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQiU4QiVBOCVFQyU5RCVCQyUyMEFQSSVFQyU5NyU5MCUyMCVFQiU4QyU4MCVFRCU5NSU5OCVFQyU5NyVBQyUyMEh5c3RyaXglRUIlQTUlQkMlMjAlRUMlQTAlODElRUMlOUElQTklMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RA==&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;HystrixCommand, HystrixObservableCommand는 내부적으로 옵저버 패턴을 이용&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJsaXN0LXVub3JkZXJlZCUyMiUyQyUyMmlzVm9pZCUyMiUzQWZhbHNlJTJDJTIyZGF0YSUyMiUzQSU3QiU3RCUyQyUyMm5vZGVzJTIyJTNBJTVCJTdCJTIyb2JqZWN0JTIyJTNBJTIyYmxvY2slMjIlMkMlMjJ0eXBlJTIyJTNBJTIybGlzdC1pdGVtJTIyJTJDJTIyaXNWb2lkJTIyJTNBZmFsc2UlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMiVFQiU4QiVBOCVFQyU5RCVCQyUyMEFQSSVFQyU5NyU5MCUyMCVFQiU4QyU4MCVFRCU5NSU5OCVFQyU5NyVBQyUyMEh5c3RyaXglRUIlQTUlQkMlMjAlRUMlQTAlODElRUMlOUElQTklMjIlMkMlMjJtYXJrcyUyMiUzQSU1QiU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RCU3RA==&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;HystrixCommand와 HystrixObservableCommand 차이는 blocking과 non-blocking의 차이&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Command 실행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;HystrixCommand 구현체에서는 execute()와 queue()를 사용할 수 있으며, 차이는 동기/비동기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;HystrixObservableCommand에서는 observe()와 toObervable()를 사용할 수 있으며, 역시 동기/비동기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;차트에서도 보이듯이 모든 실행 메소드를 실행해도 결국 toObservable() 메소드를 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Command에 대한 스레드를 생성해서 비동기적으로 실행시켰고, 응답에 대해 Fallback 처리할 수 있도록 구독&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;캐시 여부 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;해당 커맨드에 캐시를 사용하고 있고, 캐시에 Response가 있다면 Observable 형태로 즉시 반환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;회로 상태 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;커맨드를 실행할때, circuit이 열렸는지(or tripped) 확인을 하고, 열려있다면 커맨드를 실행하지 않고 즉시 getFallback()을 실행&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용가능한 Thread Pool/Queue/Semaphore가 있는지 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;커맨드의 스레드풀(or 세마포어)과 큐가 가득찼다면, 커맨드를 실행하지 않고 즉시 getFallback() 실행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;circuit-breaker가 발생한 상황이나, construct() 혹은 run() 발생에서 실패가 된 경우에는 사용자가 구현한 getFallback() 또는 resumeWithFallback() 을 실행&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HystrixObservableCommand.construc(), 혹은&lt;span&gt;&amp;nbsp;&lt;/span&gt;HystrixCommand.run()&lt;span&gt;&amp;nbsp;&lt;/span&gt;실행&lt;/li&gt;
&lt;li&gt;회로 상태 연산(Calculate circuit health)&lt;/li&gt;
&lt;li&gt;fallback 실행&lt;/li&gt;
&lt;li&gt;응답 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;Hystrix-Circuit-Breaker-구현&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Hystrix Circuit Breaker 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1041&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eyFWdd/btq65d7MDQr/UYfazuYa6HkCQklrMvcS2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eyFWdd/btq65d7MDQr/UYfazuYa6HkCQklrMvcS2K/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://github.com/Netflix/Hystrix/wiki/How-it-Works&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eyFWdd/btq65d7MDQr/UYfazuYa6HkCQklrMvcS2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeyFWdd%2Fbtq65d7MDQr%2FUYfazuYa6HkCQklrMvcS2K%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1041&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://github.com/Netflix/Hystrix/wiki/How-it-Works&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;circuit health check를 위한 최소한의 요청이 있을 때(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold())&lt;/li&gt;
&lt;li&gt;그리고, 지정한 오류율을 초과했을 때(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage())&lt;/li&gt;
&lt;li&gt;회로의 상태를&lt;span&gt;&amp;nbsp;&lt;/span&gt;CLOSED에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;OPEN으로 변경&lt;/li&gt;
&lt;li&gt;회로가 열린 동안, 모든 요청에 대해서 fallback method을 바로 실행&lt;/li&gt;
&lt;li&gt;일정 시간이 지난 후(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()), 하나의 요청을 원래 method로 실행(HALF OPEN). 이 요청이 실패한다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;OPEN으로 두고, 이 요청이 성공한다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;CLOSED로 상태를 변경. 다시 1번으로 돌아감.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;기본-설정&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 설정&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;metrics.rollingStats.timeInMilliseconds&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 오류 감시 시간, 기본값 10초&lt;/li&gt;
&lt;li&gt;circuitBreaker.requestVolumeThreshold&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 감시 시간 내 요청 수, 기본값 20&lt;/li&gt;
&lt;li&gt;circuitBreaker.errorThresholdPercentage&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 요청 대비 오류율, 기본값 50&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;감시시간 내(30초)에, 20번 이상의 요청이 있었고, 그 중에서 오류율이 50% 이상일 때 Circuit Breaker가 작동한다(circuit open)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;감시 시간 내에 요청이 반드시 20번 이상이 있어야 회로가 열림. 30초 동안 요청이 19번이었고 모두 실패했어도 Circuit Breaker는 작동하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;실습&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;1. 의존성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623590315972&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}&quot;
    }
}

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') // 1. To use spring-cloud-hystrix
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. yml을 이용한 설정&lt;/p&gt;
&lt;pre id=&quot;code_1623590771358&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hystrix:
  command:
    productInfo:    # command key. use 'default' for global setting.
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
      circuitBreaker:
        requestVolumeThreshold: 1   # Minimum number of request to calculate circuit breaker's health. default 20
        errorThresholdPercentage: 50 # Error percentage to open circuit. default 50&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. command 객체별 개별 설정&lt;/p&gt;
&lt;pre id=&quot;code_1623590956254&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserResource {
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = &quot;execution.isolation.thread.timeoutInMilliseconds&quot;, value = &quot;500&quot;)
        },
        threadPoolProperties = {
            @HystrixProperty(name = &quot;coreSize&quot;, value = &quot;30&quot;),
            @HystrixProperty(name = &quot;maxQueueSize&quot;, value = &quot;101&quot;),
            @HystrixProperty(name = &quot;keepAliveTimeMinutes&quot;, value = &quot;2&quot;),
            @HystrixProperty(name = &quot;queueSizeRejectionThreshold&quot;, value = &quot;15&quot;),
            @HystrixProperty(name = &quot;metrics.rollingStats.numBuckets&quot;, value = &quot;12&quot;),
            @HystrixProperty(name = &quot;metrics.rollingStats.timeInMilliseconds&quot;, value = &quot;1440&quot;)})
    public User getUserById(String id) {
        return userResource.getUserById(id);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 기본 설정 적용&lt;/p&gt;
&lt;pre id=&quot;code_1623590507435&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableCircuitBreaker
public class DisplayApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. command 객체 설정&lt;/p&gt;
&lt;pre id=&quot;code_1623590891859&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class ProductRemoteServiceImpl implements ProductRemoteService {

    private static final String url = &quot;http://localhost:8082/products/&quot;;
    private final RestTemplate restTemplate;

    public ProductRemoteServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    @HystrixCommand(commandKey = &quot;productInfo&quot;, fallbackMethod = &quot;getProductInfoFallback&quot;)
    public String getProductInfo(String productId) {
        return this.restTemplate.getForObject(url + productId, String.class);
    }

    public String getProductInfoFallback(String productId, Throwable t) {
        System.out.println(&quot;t = &quot; + t);
        return &quot;[ this product is sold out ]&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 테스트&lt;/p&gt;
&lt;pre id=&quot;code_1623590716765&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/products&quot;)
public class ProductController {

    @GetMapping(path = &quot;{productId}&quot;)
    public String getProductInfo(@PathVariable String productId) {
        return &quot;[product id = &quot; + productId + &quot; at &quot; + System.currentTimeMillis() + &quot;]&quot;;

//        타임아웃 테스트
//        try {
//            Thread.sleep(2000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

//      익셉션 테스트
//      throw new RuntimeException(&quot;I/O Exception&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/whdals7337/spring-cloud-workshop&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/whdals7337/spring-cloud-workshop&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623590243482&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;whdals7337/spring-cloud-workshop&quot; data-og-description=&quot;SK planet Tacademy - Spring Cloud Workshop 강의 실습 및 추가 개발 - whdals7337/spring-cloud-workshop&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/whdals7337/spring-cloud-workshop&quot; data-og-url=&quot;https://github.com/whdals7337/spring-cloud-workshop&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qwUAy/hyKy5U8EkL/25XKTkrdzkSYBVKyFsSuYk/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_121_1047_205&quot;&gt;&lt;a href=&quot;https://github.com/whdals7337/spring-cloud-workshop&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/whdals7337/spring-cloud-workshop&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qwUAy/hyKy5U8EkL/25XKTkrdzkSYBVKyFsSuYk/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_121_1047_205');&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;whdals7337/spring-cloud-workshop&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SK planet Tacademy - Spring Cloud Workshop 강의 실습 및 추가 개발 - whdals7337/spring-cloud-workshop&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=iHHuYGdG_Yk&quot;&gt;https://www.youtube.com/watch?v=iHHuYGdG_Yk&lt;/a&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://supawer0728.github.io/2018/03/11/Spring-Cloud-Hystrix/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://supawer0728.github.io/2018/03/11/Spring-Cloud-Hystrix/&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Spring Cloud</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/226</guid>
      <comments>https://wellbell.tistory.com/226#entry226comment</comments>
      <pubDate>Sun, 13 Jun 2021 22:31:39 +0900</pubDate>
    </item>
    <item>
      <title>14. 프로젝트 도식화</title>
      <link>https://wellbell.tistory.com/225</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;755&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MPs9k/btq6Wu9Xmeu/wtbK68YVmszHzyckYAuU0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MPs9k/btq6Wu9Xmeu/wtbK68YVmszHzyckYAuU0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MPs9k/btq6Wu9Xmeu/wtbK68YVmszHzyckYAuU0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMPs9k%2Fbtq6Wu9Xmeu%2FwtbK68YVmszHzyckYAuU0K%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;755&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;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/225</guid>
      <comments>https://wellbell.tistory.com/225#entry225comment</comments>
      <pubDate>Fri, 11 Jun 2021 05:24:44 +0900</pubDate>
    </item>
    <item>
      <title>13. application.yml 파일 travis에서 암호화</title>
      <link>https://wellbell.tistory.com/224</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;상황&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3 관련 비밀 키를 포함하는 yml 파일 깃허브에 그대로 올릴 수 없는 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Travis에서 파일을 암호화를 하여 깃허브에 올리고 travis에서 사용할 때는 파일을 복호화 해서 사용하게 만들 수 있습니다.&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;준비&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;windwow말고 리눅스나 맥os를 준비 해주세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window에서 암호화하고 travis에서 사용할려고하면 bad encrypt 오류가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/travis-ci/travis-ci/issues/4746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/travis-ci/travis-ci/issues/4746&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 우선 컴퓨터에 루비를 다운로드합니다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1623354604312&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get update

sudo apt install ruby&lt;/code&gt;&lt;/pre&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. 루비를 이용해서 travis를 다운로드 합니다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1623354652573&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo gem install travis&lt;/code&gt;&lt;/pre&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. travis 로그인 해줍시다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인 계정 깃허브 토큰 만들어준 뒤&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;826&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd6alc/btq6ZUtbzge/L6bMDn3l94huZJO5zMyzcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd6alc/btq6ZUtbzge/L6bMDn3l94huZJO5zMyzcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd6alc/btq6ZUtbzge/L6bMDn3l94huZJO5zMyzcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd6alc%2Fbtq6ZUtbzge%2FL6bMDn3l94huZJO5zMyzcK%2Fimg.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;826&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 토큰을 이용해서 로그인해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1623354910384&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;travis login --pro --github-token [본인 계정 토큰]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;60&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4b6jM/btq6YmDAdy4/oFeTE375ZLk1wnNZYKdn7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4b6jM/btq6YmDAdy4/oFeTE375ZLk1wnNZYKdn7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4b6jM/btq6YmDAdy4/oFeTE375ZLk1wnNZYKdn7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4b6jM%2Fbtq6YmDAdy4%2FoFeTE375ZLk1wnNZYKdn7K%2Fimg.png&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;60&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. travis 기본 설정 수정하기 매우 중요!!&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1623355279203&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vim ~/.travis/config.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;travis 를 다운받고 로그인하면 기본 endpoint가 아래 처럼 org가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 travis-ci.com를 사용하기 때문에 해당 endpoint를 com으로 변경해주어야 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;45&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw4pF5/btq61mWy8iP/lPgobck6OagChcP35Kgfsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw4pF5/btq61mWy8iP/lPgobck6OagChcP35Kgfsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw4pF5/btq61mWy8iP/lPgobck6OagChcP35Kgfsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw4pF5%2Fbtq61mWy8iP%2FlPgobck6OagChcP35Kgfsk%2Fimg.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;45&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;안맞춰 주면 아래처럼 나중에 iv undefined가 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;234&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c92fwb/btq60NfMmc1/Eg1RMXlxb5vUsZAtDL0KK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c92fwb/btq60NfMmc1/Eg1RMXlxb5vUsZAtDL0KK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c92fwb/btq60NfMmc1/Eg1RMXlxb5vUsZAtDL0KK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc92fwb%2Fbtq60NfMmc1%2FEg1RMXlxb5vUsZAtDL0KK1%2Fimg.png&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;234&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;pre id=&quot;code_1623355314319&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ openssl aes-256-cbc -K $encrypted_f4314cc5f076_key -iv $encrypted_f4314cc5f076_iv -in application-s3.yml.enc -out application-s3.yml -d
iv undefined
The command &quot;openssl aes-256-cbc -K $encrypted_f4314cc5f076_key -iv $encrypted_f4314cc5f076_iv -in application-s3.yml.enc -out application-s3.yml -d&quot; failed and exited with 1 during .&lt;/code&gt;&lt;/pre&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;5. 암호화 하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화 하고자하는 파일 위치로 이동 후 암호화를 진행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1623355533070&quot; class=&quot;cs&quot; data-ke-language=&quot;cs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;travis encrypt-file application-s3.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;885&quot; data-origin-height=&quot;308&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuFRZk/btq62XvlEsY/dT0cZI1SfkuSa08u5VVo9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuFRZk/btq62XvlEsY/dT0cZI1SfkuSa08u5VVo9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuFRZk/btq62XvlEsY/dT0cZI1SfkuSa08u5VVo9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuFRZk%2Fbtq62XvlEsY%2FdT0cZI1SfkuSa08u5VVo9K%2Fimg.png&quot; data-origin-width=&quot;885&quot; data-origin-height=&quot;308&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;우선 확인해야하는 것으로 환경변수가 자동으로 생성 되었는지 확인합니다. 만약 안되었다면 4번의 기본 설정을 제대로 안하였을 가능성이 높습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;307&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AbfFq/btq60kSsFoY/Pq5s1g1xLj905bk1mpCba0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AbfFq/btq60kSsFoY/Pq5s1g1xLj905bk1mpCba0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AbfFq/btq60kSsFoY/Pq5s1g1xLj905bk1mpCba0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAbfFq%2Fbtq60kSsFoY%2FPq5s1g1xLj905bk1mpCba0%2Fimg.png&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;307&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;적혀있는 데로 .travis.yml 파일의 before_install에 문구를 추가 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;59&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM30z7/btq6Wg4PJnF/KYc2BZS6NirAn1xHeRrKJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM30z7/btq6Wg4PJnF/KYc2BZS6NirAn1xHeRrKJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM30z7/btq6Wg4PJnF/KYc2BZS6NirAn1xHeRrKJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM30z7%2Fbtq6Wg4PJnF%2FKYc2BZS6NirAn1xHeRrKJ1%2Fimg.png&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;59&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;그 다음 줄울 보면 암호화 된 .enc 파일을 git 리포지터리에 올리라고 되어있는데 그전에 enc파일을 travis.yml에 같은 root 디렉토리로 이동시켜 줍니다. 안그러면 인식을 못하더라구요 ..ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(root로 파일을 이동시켜도 복호화 하면 암호화 했던 폴더로 돌아가는지 다시 파일 이동시키는 스크립트 안짜도되는 듯 합니다 .ㅎㅎ)&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;이제 깃허브에 푸시 해주시면 travis에서 정상적으로 사용하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;55&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSaph/btq61Loo5cL/d3InNbi73jKNlP2MeJsgNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSaph/btq61Loo5cL/d3InNbi73jKNlP2MeJsgNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSaph/btq61Loo5cL/d3InNbi73jKNlP2MeJsgNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSaph%2Fbtq61Loo5cL%2Fd3InNbi73jKNlP2MeJsgNK%2Fimg.png&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;55&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;339&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAYdpl/btq6Ymp6hn0/ncs8M85k3roOfo6RkxNVjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAYdpl/btq6Ymp6hn0/ncs8M85k3roOfo6RkxNVjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAYdpl/btq6Ymp6hn0/ncs8M85k3roOfo6RkxNVjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAYdpl%2Fbtq6Ymp6hn0%2Fncs8M85k3roOfo6RkxNVjK%2Fimg.png&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;339&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;밤 12시 쯤 부터 진행하는데 새벽 4시 되서야 해결 방법을 발견하게 되었습니다 .. 후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 설마하면서 org 사이트 로그인 했는데 아래같은 환경 변수가 20개 가까이 있는거보고 현타가 ...후 ..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;87&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmoZDK/btq6YmXU3po/LfalOfKmu0JAcxGkMYilK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmoZDK/btq6YmXU3po/LfalOfKmu0JAcxGkMYilK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmoZDK/btq6YmXU3po/LfalOfKmu0JAcxGkMYilK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmoZDK%2Fbtq6YmXU3po%2FLfalOfKmu0JAcxGkMYilK0%2Fimg.png&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;87&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/whdals7337/myintroduce-backend-project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/whdals7337/myintroduce-backend-project&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623356491032&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;whdals7337/myintroduce-backend-project&quot; data-og-description=&quot;자기소개 REST API. Contribute to whdals7337/myintroduce-backend-project development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/whdals7337/myintroduce-backend-project&quot; data-og-url=&quot;https://github.com/whdals7337/myintroduce-backend-project&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dcY7uj/hyKwBAIRDd/yBzECkyhPNtYbsM2dEYRpk/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_121_1047_205&quot;&gt;&lt;a href=&quot;https://github.com/whdals7337/myintroduce-backend-project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/whdals7337/myintroduce-backend-project&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dcY7uj/hyKwBAIRDd/yBzECkyhPNtYbsM2dEYRpk/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_121_1047_205');&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;whdals7337/myintroduce-backend-project&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자기소개 REST API. Contribute to whdals7337/myintroduce-backend-project development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/224</guid>
      <comments>https://wellbell.tistory.com/224#entry224comment</comments>
      <pubDate>Fri, 11 Jun 2021 05:21:39 +0900</pubDate>
    </item>
    <item>
      <title>12. 프론트엔드 리액트 프로젝트를 S3 + CloudFront에 배포하기</title>
      <link>https://wellbell.tistory.com/222</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기존 프론트엔드 서버가 만료되면서 서비스를 종료했었는데 백엔드 서버를 운영하고있는 AWS 프리티어 계정을 통해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3, CloudFront에 배포하여 서비스를 재개 해보려고 합니다.&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;a href=&quot;https://dev.kyoungah.com/2020-07-26-etc-aws-s3-hosting/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dev.kyoungah.com/2020-07-26-etc-aws-s3-hosting/&lt;/a&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;블로그에서는 리액트 프로젝트 스크립트에 aws s3로 보내는 스크립트를 추가해서 처리하는 반면에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 github에 psuh 되면 travis ci 에서 테스트 하고 빌드한 뒤 빌드 폴더 내 파일과 폴더를 s3로 전송해주게 됩니다.&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;size18&quot;&gt;&lt;b&gt;1. aws s3 에서 정적 호스팅을 하기위해서는 bulid 디렉토리가 아니라 안에 파일과 폴더를 넣어놔야한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌보면 당연한건데 이전 aws ec2에 배포할때 build 디렉토리 단위로 deploy를 진행하다보니 막혔던 부분입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;218&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XUYMq/btq6Ma33AQ0/v0dOiGiB1PmP9fkFAFFia1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XUYMq/btq6Ma33AQ0/v0dOiGiB1PmP9fkFAFFia1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XUYMq/btq6Ma33AQ0/v0dOiGiB1PmP9fkFAFFia1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXUYMq%2Fbtq6Ma33AQ0%2Fv0dOiGiB1PmP9fkFAFFia1%2Fimg.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;218&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 외부 도메인을 aws route 53에서 사용하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Route 53에 도메인 영역을 만들면 4개의 도메인을 주는데 이를 카페 24에 등록하면 끝&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;82&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnnhPf/btq6MT8N2qO/dMWtqlSegpwiJLJB1gAnC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnnhPf/btq6MT8N2qO/dMWtqlSegpwiJLJB1gAnC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnnhPf/btq6MT8N2qO/dMWtqlSegpwiJLJB1gAnC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnnhPf%2Fbtq6MT8N2qO%2FdMWtqlSegpwiJLJB1gAnC1%2Fimg.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;82&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;357&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbkqZ6/btq6Qt2BRcm/9e4oq1h8HZmackzqXu3g51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbkqZ6/btq6Qt2BRcm/9e4oq1h8HZmackzqXu3g51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbkqZ6/btq6Qt2BRcm/9e4oq1h8HZmackzqXu3g51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbkqZ6%2Fbtq6Qt2BRcm%2F9e4oq1h8HZmackzqXu3g51%2Fimg.png&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;357&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경하기 눌러서 인증하고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;383&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfeWMA/btq6QM8H0Fh/6dzGpV8UOvvvBY1krE6BVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfeWMA/btq6QM8H0Fh/6dzGpV8UOvvvBY1krE6BVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfeWMA/btq6QM8H0Fh/6dzGpV8UOvvvBY1krE6BVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfeWMA%2Fbtq6QM8H0Fh%2F6dzGpV8UOvvvBY1krE6BVK%2Fimg.png&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;383&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;순서에 맞게 입력 후 ip확인 다 눌러준 뒤 변경하기 누르고 대기하면됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제가 여기서 안됬는데 이유를 알고 보니 aws 주소들이 &lt;span style=&quot;background-color: #fafafa; color: #16191f;&quot;&gt;ns-~~~~.com. 이런식으로 마지막에 .이 찍혀있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지우고 해주시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;305&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3ihyL/btq6PDLbU98/dPFKK20bApxYRotO2UmIIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3ihyL/btq6PDLbU98/dPFKK20bApxYRotO2UmIIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3ihyL/btq6PDLbU98/dPFKK20bApxYRotO2UmIIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3ihyL%2Fbtq6PDLbU98%2FdPFKK20bApxYRotO2UmIIk%2Fimg.png&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;305&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.Route 53와 S3 연동시 이름 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 문제는 상상도 못했던 일인데 아래처럼 2개의&amp;nbsp; S3가 있더라도&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;119&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXHutw/btq6LeMCeU2/6dzNI1nL4aO6BZcq12GGD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXHutw/btq6LeMCeU2/6dzNI1nL4aO6BZcq12GGD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXHutw/btq6LeMCeU2/6dzNI1nL4aO6BZcq12GGD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXHutw%2Fbtq6LeMCeU2%2F6dzNI1nL4aO6BZcq12GGD0%2Fimg.png&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;119&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;실제 뜨는건 wellbell.co.kr s3 만 뜨는데 이유를 찾아보니 도메인이랑 이름이 같은 s3 버킷만 사용이 가능하다고합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;386&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdN04k/btq6MTHLG50/ukoosC7x00zxKdIlf0vLZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdN04k/btq6MTHLG50/ukoosC7x00zxKdIlf0vLZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdN04k/btq6MTHLG50/ukoosC7x00zxKdIlf0vLZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdN04k%2Fbtq6MTHLG50%2FukoosC7x00zxKdIlf0vLZ0%2Fimg.png&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;386&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/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;size18&quot;&gt;&lt;b&gt;4. https 적용시 에러 발생&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;42&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mojmv/btq6QPqQxAH/0eCtKKjSBamo01jFee0v1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mojmv/btq6QPqQxAH/0eCtKKjSBamo01jFee0v1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mojmv/btq6QPqQxAH/0eCtKKjSBamo01jFee0v1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMojmv%2Fbtq6QPqQxAH%2F0eCtKKjSBamo01jFee0v1K%2Fimg.png&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;42&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;에러를 읽어보면 간단하게 https인대 내부 적으로 http 요청을 보내는데 이거 막았다. 이정도로 이해하시면 될거같습니다. 고치는 방법은 2가지 정도로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 그냥 http로 접속한다. 수정 할거 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내부적으로 사용하는 요청들을 https로 바꾼다. 이거 백엔드 서버에&amp;nbsp; https를 적용...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 1번으로 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 서브 도메인 적용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.wellbell.co.kr&quot;&gt;www.wellbell.co.kr&lt;/a&gt; 서브 도메인을 적용하려고 진행하는 방법을 찾기가 생각 보다 어려웠는데 해결 방법은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 서브도메인 이름으로 s3 버킷을 하나더 만들고&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4wZG7/btq6Q67cuEH/yMJj63O7ZKHQ9KEvOQJUy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4wZG7/btq6Q67cuEH/yMJj63O7ZKHQ9KEvOQJUy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4wZG7/btq6Q67cuEH/yMJj63O7ZKHQ9KEvOQJUy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4wZG7%2Fbtq6Q67cuEH%2FyMJj63O7ZKHQ9KEvOQJUy0%2Fimg.png&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;301&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;2) route 53에 레코드 등록하고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;424&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNOkej/btq6QsJGMnh/KuhENeHn9vIFVnbAs8Vkh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNOkej/btq6QsJGMnh/KuhENeHn9vIFVnbAs8Vkh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNOkej/btq6QsJGMnh/KuhENeHn9vIFVnbAs8Vkh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNOkej%2Fbtq6QsJGMnh%2FKuhENeHn9vIFVnbAs8Vkh0%2Fimg.png&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;424&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;3) 정적 웹사이트 호스팅을 활성화 한 뒤 메인 도메인으로 리다이렉트 해주면된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;657&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vHz4y/btq6PaWQONI/KcEDTp5rgliMn8w9ILqo7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vHz4y/btq6PaWQONI/KcEDTp5rgliMn8w9ILqo7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vHz4y/btq6PaWQONI/KcEDTp5rgliMn8w9ILqo7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvHz4y%2Fbtq6PaWQONI%2FKcEDTp5rgliMn8w9ILqo7K%2Fimg.png&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;657&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;적용과정에서 s3만으로도 정적 웹사이트 호스팅이 가능한데 cloudfront를 왜 적용하지라는 생각이 들어서 해당 부분을 공부하고 정리해서 글을 쓰도록 하겠습니다. 그럼 이만 ..&lt;/p&gt;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/222</guid>
      <comments>https://wellbell.tistory.com/222#entry222comment</comments>
      <pubDate>Tue, 8 Jun 2021 14:35:26 +0900</pubDate>
    </item>
    <item>
      <title>11. [샘플] 이미지 파일 서버에서 AWS S3로 업로드 및 다운로드 받기</title>
      <link>https://wellbell.tistory.com/221</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기존 자기소개 페이지가 이미지 업로드시 서버의 특정 폴더에 저장하게 개발되어있다보니 만약 다른 서버를 여러대를 두고 로드밸런싱한다면 이미지가 없어서 문제가 생길 수 있다고 판단 하였으며 이를 위해서 파일을 S3에 업로드하고 S3에 접근해서 해당 파일을 쓸수 있도록 진행할려고 생각하였습니다.&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;현재 실습 진행은 완료된 상태로 포스팅을 준비중입니다. 관련해서 코드를 먼저 보고 싶으시다면 아래를 참조해주시면 될거같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 아래 코드 뿐만아니라 S3 버킷 만들고 IAM 만들어서 설정을 해주는 등 부가적인 부분이 많습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/whdals7337/springboot-aws-s3-project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/whdals7337/springboot-aws-s3-project&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623115720038&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;whdals7337/springboot-aws-s3-project&quot; data-og-description=&quot;파일을 S3 서버에 업로드, 삭제 및 업로드 받아서 사용하도록 S3 서버 구축 및 사용 실습 - whdals7337/springboot-aws-s3-project&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/whdals7337/springboot-aws-s3-project&quot; data-og-url=&quot;https://github.com/whdals7337/springboot-aws-s3-project&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btxJIa/hyKuAbutcK/eCBspz52mMipxMgRjrENfK/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_121_1047_205&quot;&gt;&lt;a href=&quot;https://github.com/whdals7337/springboot-aws-s3-project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/whdals7337/springboot-aws-s3-project&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btxJIa/hyKuAbutcK/eCBspz52mMipxMgRjrENfK/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_121_1047_205');&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;whdals7337/springboot-aws-s3-project&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;파일을 S3 서버에 업로드, 삭제 및 업로드 받아서 사용하도록 S3 서버 구축 및 사용 실습 - whdals7337/springboot-aws-s3-project&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자기소개페이지만들기</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/221</guid>
      <comments>https://wellbell.tistory.com/221#entry221comment</comments>
      <pubDate>Tue, 8 Jun 2021 10:29:40 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 셔틀버스 문제 (자바)</title>
      <link>https://wellbell.tistory.com/220</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크루들을 우선순위 큐에 전부 집어놓고&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;1 . 문자열의 시간을 숫자로 변경해서 계산하기 편하게 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 마지막에 탑승한 사람 - 1분이 도착해야하는 사람임으로 탑승할때 마다 해당 값을 적용&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ps&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 숫자를 문자로 변경할때 3항연산자 2개를 사용했는데 String.format() 메서드를 사용하면 편하다.&lt;/p&gt;
&lt;pre id=&quot;code_1623075444545&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.List;
import java.util.PriorityQueue;

// 프로그래머스 셔틀버스 문제
class Solution {
    public String solution(int n, int t, int m, String[] timetable) {
        int answerTime = 0;
        int departTime = 9 * 60;
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; buses = new ArrayList&amp;lt;&amp;gt;();
        for (int i = 0 ; i &amp;lt; n; i++) {
            buses.add(new ArrayList&amp;lt;&amp;gt;());
        }

        PriorityQueue&amp;lt;Integer&amp;gt; crews = new PriorityQueue&amp;lt;&amp;gt;();
        for (String time : timetable) {
            StringConvertTime(time);
            crews.offer(StringConvertTime(time));
        }

        for (int i = 0; i &amp;lt; n; i++) {
            while (!crews.isEmpty()) {
                int crew = crews.poll();
                if (crew &amp;lt;= departTime &amp;amp;&amp;amp; buses.get(i).size() &amp;lt; m) {
                    buses.get(i).add(crew);
                    answerTime = crew - 1;
                }else {
                    crews.offer(crew);
                    break;
                }
            }
            departTime += t;
        }

        if (buses.get(n - 1).size() &amp;lt; m) {
            answerTime = departTime - t;
        }
        return TimeConvertString(answerTime);
    }

    static int StringConvertTime(String s) {
        String[] split = s.split(&quot;:&quot;);
        int hour = Integer.parseInt(split[0]);
        int minute = Integer.parseInt(split[1]);
        return hour * 60 + minute;
    }

    static String TimeConvertString(int val) {
        String hStr = String.format(&quot;%02d&quot;, val / 60);
        String mStr = String.format(&quot;%02d&quot;, val % 60);
        return hStr +&quot;:&quot;+mStr;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/220</guid>
      <comments>https://wellbell.tistory.com/220#entry220comment</comments>
      <pubDate>Mon, 7 Jun 2021 23:22:56 +0900</pubDate>
    </item>
    <item>
      <title>정적 팩토리 메서드 패턴</title>
      <link>https://wellbell.tistory.com/219</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;객체 생성을 캡술화 하는 기법으로 좀더 구체적으로 객체를 생성하는 메서드를 만들고 static으로 선언하는 기법이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름이 있으므로 생성자에 비해 가독성이 좋다.&lt;/li&gt;
&lt;li&gt;호출할 때마다 새로운 객체를 생성할 필요가 없다.&lt;/li&gt;
&lt;li&gt;하위 자료형 객체를 반환할 수 있다.&lt;/li&gt;
&lt;li&gt;객체 생성을 캡슐화할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ol 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;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 장단점이 있는데 하나 씩 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;4개의 정적 팩토리 메서드 패턴을 만들었습니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1623037762526&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Character {
    int STR;
    int DEX;
    int INT;
    int LUK;

    public Character(int STR, int DEX, int INT, int LUK) {
        this.STR = STR;
        this.DEX = DEX;
        this.INT = INT;
        this.LUK = LUK;
    }

    public static Character newWarrior() {
        return new Character(15, 10,4,4);
    }

    public static Character newArcher() {
        return new Character(10, 15,4,4);
    }

    public static Character newMage() {
        return new Character(4, 4,15,10);
    }

    public static Character newThief() {
        return new Character(4, 10,4,15);
    }
}&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;pre id=&quot;code_1623038215337&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 생성자
Character warrior1 = new Character(15, 10,4,4);
Character archer1 = new Character(10, 15,4,4);
Character mage1 = new Character(4, 4,15,10);
Character thief1 = new Character(4, 10,4,15);

// 정적 팩토리 메서드
Character warrior2 = Character.newWarrior();
Character archer2 = Character.newArcher();
Character mage2 = Character.newMage();
Character thief2 = Character.newThief();&lt;/code&gt;&lt;/pre&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. 호출시 새로운 객체 생성&amp;nbsp; X&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;immutable 객체를 캐시해서 사용한 다면 굳이 일일이 new같은 연산을 사용할 필요가 없어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예로 BigInteger가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사적에 0일 생성하고 valueOf 라는 정적 팩토리 메서드 패턴의 메서드를 이용하여 이를 리턴하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1623038405395&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static final BigInteger ZERO = new BigInteger(new int[0], 0);

public static BigInteger valueOf(long val) {
        // If -MAX_CONSTANT &amp;lt; val &amp;lt; MAX_CONSTANT, return stashed constant
        if (val == 0)
            return ZERO;
        if (val &amp;gt; 0 &amp;amp;&amp;amp; val &amp;lt;= MAX_CONSTANT)
            return posConst[(int) val];
        else if (val &amp;lt; 0 &amp;amp;&amp;amp; val &amp;gt;= -MAX_CONSTANT)
            return negConst[(int) -val];

        return new BigInteger(val);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&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;pre id=&quot;code_1623038754485&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class OrderUtil {

    public static Discount createDiscountItem(String discountCode) throws Exception {
        if(!isValidCode(discountCode)) {
            throw new Exception(&quot;잘못된 할인 코드&quot;);
        }
        // 쿠폰 코드인가? 포인트 코드인가?
        if(isUsableCoupon(discountCode)) {
            return new Coupon(1000);
        } else if(isUsablePoint(discountCode)) {
            return new Point(500);
        }
        throw new Exception(&quot;이미 사용한 코드&quot;);
    }
}

class Coupon extends Discount { }
class Point extends Discount { }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&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;아래는 예시는 &lt;span style=&quot;color: #222426;&quot;&gt;DTO와 Entity간에는 형 변환에 관한 부분으로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222426;&quot;&gt;생성자를 사용하는 경우 외부에 생성자 내부 구현을 드러낸 채 사용한다면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222426;&quot;&gt;정적 팩토리 메서드 패턴을 사용하면 내부 구현을 숨겨서 사용할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623039080979&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CarDto {
  private String name;
  private int position;
  
  pulbic static CarDto from(Car car) {
    return new CarDto(car.getName(), car.getPosition());
  }
}


CarDto carDto = new CarDto(car.getName(), car.getPosition); // 생성자를 쓴 경우
Car carDto = CarDto.from(car); // 정적 팩토리 메서드를 쓴 경우&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;정적 팩토리 메서드 패턴의 네이밍 컨벤션&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 단점으로 &lt;span style=&quot;background-color: #fafafa; color: #4d5667;&quot;&gt;정적 팩토리 메서드는 다른 정적 메서드와 잘 구분되지 않는다. 라고 하였는데 이 때문에 구분을 쉽게 하기 위해서 네이밍 컨벤션이 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;from&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 하나의 매개 변수를 받아서 객체를 생성&lt;/li&gt;
&lt;li&gt;of&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 여러개의 매개 변수를 받아서 객체를 생성&lt;/li&gt;
&lt;li&gt;getInstance&lt;span&gt;&amp;nbsp;&lt;/span&gt;|&lt;span&gt;&amp;nbsp;&lt;/span&gt;instance&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.&lt;/li&gt;
&lt;li&gt;newInstance&lt;span&gt;&amp;nbsp;&lt;/span&gt;|&lt;span&gt;&amp;nbsp;&lt;/span&gt;create&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 새로운 인스턴스를 생성&lt;/li&gt;
&lt;li&gt;get[OtherType]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.&lt;/li&gt;
&lt;li&gt;new[OtherType]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 다른 타입의 새로운 인스턴스를 생성.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ps&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;롬복에서는 아래처럼 정적 팩토리 메서드 패턴을 쉽게 만들 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1623039247030&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor(staticName = &quot;of&quot;)
public class BlogUser {
  private final Long id;
  private final String name;
}&lt;/code&gt;&lt;/pre&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;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://johngrib.github.io/wiki/static-factory-method-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://johngrib.github.io/wiki/static-factory-method-pattern/&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://velog.io/@ljinsk3/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@ljinsk3/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Design Pattern/기타</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/219</guid>
      <comments>https://wellbell.tistory.com/219#entry219comment</comments>
      <pubDate>Mon, 7 Jun 2021 13:18:12 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 배달 문제 (자바)</title>
      <link>https://wellbell.tistory.com/218</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12978&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12978&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622991905927&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;5 [[1,2,1],[2,3,3],[5,2,2],[1,4,2],[5,3,1],[5,4,2]] 3 4 6 [[1,2,1],[1,3,2],[2,3,2],[3,4,3],[3,5,2],[3,5,3],[5,6,1]] 4 4&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12978&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12978&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b3KMbb/hyKutowoOX/1O7WTI3rJMqXuB0lKAhRK1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bIy2Ab/hyKs4jBut9/LUjUCSNtw48t1BsjJ5Fka1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/xnOd1/hyKs3LLDZP/vjh4alf7vfuxOHBClhATpK/img.png?width=250&amp;amp;height=212&amp;amp;face=0_0_250_212&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12978&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12978&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b3KMbb/hyKutowoOX/1O7WTI3rJMqXuB0lKAhRK1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bIy2Ab/hyKs4jBut9/LUjUCSNtw48t1BsjJ5Fka1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/xnOd1/hyKs3LLDZP/vjh4alf7vfuxOHBClhATpK/img.png?width=250&amp;amp;height=212&amp;amp;face=0_0_250_212');&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;5 [[1,2,1],[2,3,3],[5,2,2],[1,4,2],[5,3,1],[5,4,2]] 3 4 6 [[1,2,1],[1,3,2],[2,3,2],[3,4,3],[3,5,2],[3,5,3],[5,6,1]] 4 4&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;다익스트라 알고리즘으로 1마을 에서 의 거리 배열을 초기화 해주고 그 중 k보다 작은 경우 count하면 해결&lt;/p&gt;
&lt;pre id=&quot;code_1622991890662&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

class Node implements Comparable&amp;lt;Node&amp;gt; {
    private final int index;
    private final int cost;

    public Node(int index, int cost) {
        this.index = index;
        this.cost = cost;
    }

    public int getIndex() {
        return index;
    }

    public int getCost() {
        return cost;
    }

    @Override
    public int compareTo(Node o) {
        return Integer.compare(this.cost, o.cost);
    }
}

// 프로그래머스 배달 문제
class Delivery {
    static List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; graph = new ArrayList&amp;lt;&amp;gt;();
    static int[] d;
    static int INF = (int) 1e9;

    public int solution(int N, int[][] road, int K) {
        d = new int[N + 1];
        Arrays.fill(d, INF);
        for (int i = 0 ; i &amp;lt;= N; i++) {
            graph.add(new ArrayList&amp;lt;&amp;gt;());
        }

        for (int[] street : road) {
            int nodeA = street[0];
            int nodeB = street[1];
            int cost = street[2];

            graph.get(nodeA).add(new Node(nodeB, cost));
            graph.get(nodeB).add(new Node(nodeA, cost));
        }

        dijkstra();

        int answer = 0;
        for (int i = 1; i &amp;lt;= N; i++) {
            if (d[i] &amp;lt;= K) {
                answer++;
            }
        }
        return answer;
    }

    static void dijkstra() {
        PriorityQueue&amp;lt;Node&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();
        pq.offer(new Node(1, 0));
        d[1] = 0;

        while (!pq.isEmpty()) {
            Node node = pq.poll();
            int nIdx = node.getIndex();
            int nCost = node.getCost();

            if (d[nIdx] &amp;lt; nCost) {
                continue;
            }

            List&amp;lt;Node&amp;gt; nodes = graph.get(nIdx);
            for (Node next : nodes) {
                int cost = next.getCost() + nCost;

                if (cost &amp;lt; d[next.getIndex()]) {
                    d[next.getIndex()] = cost;
                    pq.offer(new Node(next.getIndex(), cost));
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/218</guid>
      <comments>https://wellbell.tistory.com/218#entry218comment</comments>
      <pubDate>Mon, 7 Jun 2021 00:05:54 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - N개의 최소공배수 문제 (자바)</title>
      <link>https://wellbell.tistory.com/217</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12953&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12953&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622989805774&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;코딩테스트 연습 - N개의 최소공배수&quot; data-og-description=&quot;두 수의 최소공배수(Least Common Multiple)란 입력된 두 수의 배수 중 공통이 되는 가장 작은 숫자를 의미합니다. 예를 들어 2와 7의 최소공배수는 14가 됩니다. 정의를 확장해서, n개의 수의 최소공배&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12953&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12953&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cWC2NG/hyKtgYCF8z/u6VSP5t3MKQxOwHBPRGeY1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/JXIWT/hyKuunpt7Z/Soxqjp7qbr7Ea9bFpBVXw1/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/12953&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12953&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cWC2NG/hyKtgYCF8z/u6VSP5t3MKQxOwHBPRGeY1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/JXIWT/hyKuunpt7Z/Soxqjp7qbr7Ea9bFpBVXw1/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;코딩테스트 연습 - N개의 최소공배수&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;두 수의 최소공배수(Least Common Multiple)란 입력된 두 수의 배수 중 공통이 되는 가장 작은 숫자를 의미합니다. 예를 들어 2와 7의 최소공배수는 14가 됩니다. 정의를 확장해서, n개의 수의 최소공배&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;해당 최소공배수와 세번째 값의 초소공배수를 구하는 방법으로&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;pre id=&quot;code_1622989822112&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int solution(int[] arr) {
        int answer = arr[0];
        for (int i = 1; i &amp;lt; arr.length; i++) {
            answer = lcm(answer, arr[i]);
        }
        return answer;
    }

    static int lcm(int a, int b) {
        return (a * b) / gcd(a, b);
    }

    static int gcd(int a, int b) {
        while (b != 0) {
            int r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/217</guid>
      <comments>https://wellbell.tistory.com/217#entry217comment</comments>
      <pubDate>Sun, 6 Jun 2021 23:32:05 +0900</pubDate>
    </item>
    <item>
      <title>자바 Enum 활용</title>
      <link>https://wellbell.tistory.com/216</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바 Enum의 장점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 안정성 향상: 객체 범위 제한&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 소스 가독성 향상: 상수의 사용 목적 및 해석 용이&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- IDE의 적극적인 지원: 자동완성, 오타검증, 텍스트 리팩토링&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enum을 어떻게 활용할 수 있을까요?&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. CODE 관리용 테이블 교체&lt;/b&gt;&lt;/h4&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;427&quot; data-origin-height=&quot;145&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TXymR/btq6AvBlI8n/xG7l5vPCsYEPA7lkEQKYs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TXymR/btq6AvBlI8n/xG7l5vPCsYEPA7lkEQKYs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TXymR/btq6AvBlI8n/xG7l5vPCsYEPA7lkEQKYs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTXymR%2Fbtq6AvBlI8n%2FxG7l5vPCsYEPA7lkEQKYs0%2Fimg.png&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;145&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;pre id=&quot;code_1622984672049&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aBox.setIfEmpty(&quot;hits&quot;, 0);
aBox.setIfEmpty(&quot;notiSt&quot;, &quot;OS01&quot;);
aBox.setIfEmpty(&quot;noticetarget&quot;, &quot;NT01&quot;);
aBox.setIfEmpty(&quot;notiType&quot;,&quot;N&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1622984799411&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
	COUNT(NO.NOTICE_SEQ) AS TOTAL_COUNT
FROM
	TB_NOTICE NO
WHERE 
	NO.NOTICE_ST = #{notiSt}&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;1. 매번 공통 코드 관리용 테이블을 조인해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 코드를 직접 하드 코딩하기 때문에 만약 OS01을 잘못적어서 OS02라고 한다면 여지없이 잘못된 데이터를 가져오게 됩니다.&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;테이블을 Enum으로 대체하고 하드 코딩을 Enum을 통해서 처리한다면 위와 같은 문제를 해결할수 있습니다. 다만 enum으로 변경하여 효율을 보기 위해서는&amp;nbsp;&lt;span style=&quot;color: #24292e;&quot;&gt;enum으로 관리되는 데이터가 빈번하게 변경(추가/제거) 되지 않아야&lt;/span&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;하는 것과 이 code enum을 다른 테이블에서도 빈번하게 사용해야 한다는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&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;span style=&quot;color: #24292e;&quot;&gt;Java8이 되면서&amp;nbsp;&lt;/span&gt;Function 인터페이스&lt;span style=&quot;color: #24292e;&quot;&gt;가 등장하게 되어&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;function을 값&lt;/span&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;으로 사용할 수 있게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;분명 코드를 간결하게 하고 계산 공식이 변하는 것이 아니라면 매우 좋은 방식이라고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;전략 패턴으로 인터페이스, 구현체 대신 사용할 수 있을거 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;370&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qeTCi/btq6Asj9cNj/miTJBZKZlNAox4Vf6qz0k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qeTCi/btq6Asj9cNj/miTJBZKZlNAox4Vf6qz0k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qeTCi/btq6Asj9cNj/miTJBZKZlNAox4Vf6qz0k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqeTCi%2Fbtq6Asj9cNj%2FmiTJBZKZlNAox4Vf6qz0k0%2Fimg.png&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;370&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;416&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTC6bf/btq6Ft9IxV1/jpyJJRHhb3xPUa5FPv7k2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTC6bf/btq6Ft9IxV1/jpyJJRHhb3xPUa5FPv7k2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTC6bf/btq6Ft9IxV1/jpyJJRHhb3xPUa5FPv7k2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTC6bf%2Fbtq6Ft9IxV1%2FjpyJJRHhb3xPUa5FPv7k2k%2Fimg.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;416&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;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;span style=&quot;color: #24292e;&quot;&gt;결제방식에 따라 다른 수수료를 부가해야하는 과정이 필요하다고 가정하겠습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;아래와 같이 결제방식에 따른 수수료 그룹이 있는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;337&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1T21w/btq6Bba7Qx9/qdkTl9u730QDrfzZBIZhB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1T21w/btq6Bba7Qx9/qdkTl9u730QDrfzZBIZhB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1T21w/btq6Bba7Qx9/qdkTl9u730QDrfzZBIZhB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1T21w%2Fbtq6Bba7Qx9%2FqdkTl9u730QDrfzZBIZhB0%2Fimg.png&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;337&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;각 결제방식이 어느 그룹에 포함되는지에 대한 분기로직&lt;/span&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;이 필요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;그룹별 로직이나 추가 메소드가 필요한 경우와 그룹을 select box로 표시해야하는 경우 등까지 고려하면 클래스나 데이터베이스로 해결하기엔 코드가 너무 분산&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;340&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQyti0/btq6BEK91C6/ld2tMfyOMmKLC6edyoYNY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQyti0/btq6BEK91C6/ld2tMfyOMmKLC6edyoYNY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQyti0/btq6BEK91C6/ld2tMfyOMmKLC6edyoYNY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQyti0%2Fbtq6BEK91C6%2Fld2tMfyOMmKLC6edyoYNY0%2Fimg.png&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;340&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;600&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ngGce/btq6BClZ1pE/kzdjKJj3nyHU6WOucCLY4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ngGce/btq6BClZ1pE/kzdjKJj3nyHU6WOucCLY4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ngGce/btq6BClZ1pE/kzdjKJj3nyHU6WOucCLY4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FngGce%2Fbtq6BClZ1pE%2FkzdjKJj3nyHU6WOucCLY4K%2Fimg.png&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;600&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;특정&amp;nbsp;&lt;/span&gt;PaymentOption&lt;span style=&quot;color: #24292e;&quot;&gt;이 있을때 이 값이 어느 그룹에 포함될지는&amp;nbsp;&lt;/span&gt;PaymentGroup&lt;span style=&quot;color: #24292e;&quot;&gt;에 직접 물어보면(&lt;/span&gt;findGroup&lt;span style=&quot;color: #24292e;&quot;&gt;) 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;각 그룹별로 추가 기능이 필요한 경우엔&amp;nbsp;&lt;/span&gt;PaymentGroup&lt;span style=&quot;color: #24292e;&quot;&gt;에 추가하면 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #24292e;&quot;&gt;또한 select box로 그룹 리스트를 출력해야하는 경우엔&amp;nbsp;&lt;/span&gt;PaymentGroup.values()&lt;span style=&quot;color: #24292e;&quot;&gt;을 사용하면 아주 쉽게 대응할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reference&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jojoldu.tistory.com/137&lt;/a&gt;&lt;/p&gt;</description>
      <category>JAVA</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/216</guid>
      <comments>https://wellbell.tistory.com/216#entry216comment</comments>
      <pubDate>Sun, 6 Jun 2021 23:02:16 +0900</pubDate>
    </item>
    <item>
      <title>자바 final 키워드</title>
      <link>https://wellbell.tistory.com/215</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;롬복에서 @RequiredArgsConstructor 어노테이션를 적용하면 private fianl로 선언된 &lt;span style=&quot;color: #000000;&quot;&gt;필드를 포함하는 생성자를 자동으로 만들어 준다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 우아한 형제들의 우아한 테크코스 과정을 진행하시는 분들의 코드를 깃허브를 통해서 보게되었는데 필드, 매개변수등 final 키워드를 적용하는 것을 보면서 final 키워드에 대해서 한번 제대로 공부해서 정리해야겠다라는 생각을 하게 되었습니다 .ㅎ&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;&lt;span style=&quot;color: #555555;&quot;&gt;final&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;final 키워드는 변수(variable), 메서드(method), 또는 클래스(class)에 사용될 수 있습니다. 이 final 키워드는 어떤 곳에 사용되냐에 따라 다른 의미를 가집니다. 하지만 final 키워드를 붙이면 무언가를 제한한다는 의미를 가지는 것은 공통적이 성격입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;변수&lt;/span&gt;&lt;/b&gt;&lt;b&gt;(variable)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수에 final을 붙이면 이 변수는 수정할 수 없다는 의미&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;를 가집니다. 수정될 수 없기 때문에 초기화 값은 필수적입니다. 만약에 객체안의 변수라면 생성자, static 블럭을 통한 초기화까지는 허용합니다.&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;49&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGXdYq/btq6AdUoApD/oxxgZAVqaBWKZbJSMDyImk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGXdYq/btq6AdUoApD/oxxgZAVqaBWKZbJSMDyImk/img.png&quot; data-alt=&quot;초기화 값은 필수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGXdYq/btq6AdUoApD/oxxgZAVqaBWKZbJSMDyImk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGXdYq%2Fbtq6AdUoApD%2FoxxgZAVqaBWKZbJSMDyImk%2Fimg.png&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;49&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;초기화 값은 필수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;140&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2Ec5b/btq6AvAvJhX/RnaLLXrHReZXUbfY2eoj3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2Ec5b/btq6AvAvJhX/RnaLLXrHReZXUbfY2eoj3K/img.png&quot; data-alt=&quot;수정 불가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2Ec5b/btq6AvAvJhX/RnaLLXrHReZXUbfY2eoj3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2Ec5b%2Fbtq6AvAvJhX%2FRnaLLXrHReZXUbfY2eoj3K%2Fimg.png&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;140&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수정 불가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 즉 참조 값의 경우 조금 헷갈리실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person 은 final 적용하여 수정이 불가능 하지만 Person 내부의 변수는 변경이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 &lt;b&gt;객체의 내부의 값은 final의 영향 밖에 있기 때문&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;600&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBK3UV/btq6ArEPi0R/hKg8hmDx0SdYRw3etKrJu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBK3UV/btq6ArEPi0R/hKg8hmDx0SdYRw3etKrJu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBK3UV/btq6ArEPi0R/hKg8hmDx0SdYRw3etKrJu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBK3UV%2Fbtq6ArEPi0R%2FhKg8hmDx0SdYRw3etKrJu1%2Fimg.png&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;600&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;물론 아래 처럼 객체 내부의 값에 final을 적용 할수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;184&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uVoQd/btq6A9p8syd/aiz89wXReS0xTrI0iklrk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uVoQd/btq6A9p8syd/aiz89wXReS0xTrI0iklrk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uVoQd/btq6A9p8syd/aiz89wXReS0xTrI0iklrk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuVoQd%2Fbtq6A9p8syd%2Faiz89wXReS0xTrI0iklrk0%2Fimg.png&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;184&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 된다면 내부 값도 수정할 수 없게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;493&quot; data-origin-height=&quot;71&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6zBgC/btq6BCySRnC/3tuFe719UncsDw8TzOjKK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6zBgC/btq6BCySRnC/3tuFe719UncsDw8TzOjKK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6zBgC/btq6BCySRnC/3tuFe719UncsDw8TzOjKK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6zBgC%2Fbtq6BCySRnC%2F3tuFe719UncsDw8TzOjKK1%2Fimg.png&quot; data-origin-width=&quot;493&quot; data-origin-height=&quot;71&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;b&gt;(method)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메서드에 final을 붙이면 override를 제한&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;b&gt;override란&lt;/b&gt; 클래스를 상속하였을때 부모의 메서드를 상속해서 재구현을 할 수 있습니다. 즉, 동일한 메서드지만 다르게 동작하게 만들 수 있다는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;이렇게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;상속 받은 클래스에서 해당 메서드를 수정해서 사용하지 못하도록 할 수 있는것이 메서드에 final을 붙이는 것&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클래스(Class)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;final 키워드를 클래스에 붙이면 상속 불가능 클래스&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;가 됩니다. 즉, 다른 클래스에서 상속하여 재정의를 할 수 없는 것입니다. &lt;span style=&quot;color: #555555;&quot;&gt;클래스 설계시 재정의 여부를 생각해서 재정의 불가능하게 사용하고 싶다면 final로 등록하는게 추후 유지보수차원에서 좋습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;개인적인 생각&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;메서드와 클래스에서 final을 사용하는 것은 상속 등 설계 영역의 고려사항이 반영되는 것이라면&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt; 변수에서 final은 개발 단계에서 특정 값이 의도치 않게 변하지 않게 해서 안전성을 높히는 역할에 비중이 있는게 아닌가 싶습니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>JAVA</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/215</guid>
      <comments>https://wellbell.tistory.com/215#entry215comment</comments>
      <pubDate>Sat, 5 Jun 2021 13:36:54 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 압축 문제 (자바)</title>
      <link>https://wellbell.tistory.com/214</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/17684&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/17684&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622797207634&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;코딩테스트 연습 - [3차] 압축&quot; data-og-description=&quot;TOBEORNOTTOBEORTOBEORNOT [20, 15, 2, 5, 15, 18, 14, 15, 20, 27, 29, 31, 36, 30, 32, 34]&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17684&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17684&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hrAK7/hyKrDszicX/a5XNHrzDAPukB6gZXCs1B1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/ziFRq/hyKs4PClEW/XBQIDiUbjcZBzx4A85AlSk/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/17684&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17684&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hrAK7/hyKrDszicX/a5XNHrzDAPukB6gZXCs1B1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/ziFRq/hyKs4PClEW/XBQIDiUbjcZBzx4A85AlSk/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;코딩테스트 연습 - [3차] 압축&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TOBEORNOTTOBEORTOBEORNOT [20, 15, 2, 5, 15, 18, 14, 15, 20, 27, 29, 31, 36, 30, 32, 34]&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;사실 본인은 아래 코드 처럼 깔끔하게 코딩을 하지 못했다. 풀고나서 다른 사람 풀이를 보고 정리한 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 이해해보자면 기존 알파벳을 전부 등록하고 문자열을 2개 씩 붙혀가면서 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정에서 고려해야하는 것은 반복문 중에 index가 마지막 위치까지 오는 경우이다.&lt;/p&gt;
&lt;pre id=&quot;code_1622796936287&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.HashMap;

// 프로그래머스 압축 문제
class Compression {

    static HashMap&amp;lt;String, Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
    static ArrayList&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
    static char[] arr;
    static int idx = 1;
    public int[] solution(String msg) {
        for(int i = 0; i &amp;lt; 26; i++){
            map.put(String.valueOf((char)('A' + i)), i + 1);
        }
        arr = msg.toCharArray();

        while(idx &amp;lt;= arr.length) {
            String tmp;
            if(idx == arr.length) tmp = String.valueOf(arr[idx-1]);
            else tmp = String.valueOf(arr[idx - 1]) + arr[idx];
            encoding(tmp);
        }

        int[] answer = new int[list.size()];
        int size = 0;
        for(int i : list) {
            answer[size++] = i;
            System.out.print(i + &quot; &quot;);
        }
        return answer;
    }
    static void encoding(String tmp) {
        int ans = 0;
        idx++;
        // 현재 문자열이 사전에 등록되었으며 남은 문자열이 있는 경우
        if(map.containsKey(tmp) &amp;amp;&amp;amp; idx &amp;lt; arr.length) {
            encoding(tmp + arr[idx]); // 다음 문자까지 확인
        }
        // 사전에 등록되어 있으면서 남은 문자열이 없는 경우
        else if(map.containsKey(tmp)){
            idx++; // 마지막 문자만 따로 계산하지 않도록 idx를 범위 밖으로
            list.add(map.get(tmp));
        }
        // 사전에 등록 되지 않은 경우
        else {
            map.put(tmp, map.size() + 1);
            tmp = tmp.substring(0, tmp.length() - 1);

            list.add(map.get(tmp));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/214</guid>
      <comments>https://wellbell.tistory.com/214#entry214comment</comments>
      <pubDate>Fri, 4 Jun 2021 18:00:14 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 스킬트리 문제 (자바)</title>
      <link>https://wellbell.tistory.com/213</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/49993&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/49993&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622796707019&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/49993&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/49993&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/M6fbV/hyKrvVp3ad/5ZKiOE31tQC3ybu0Uiz840/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/FG2SP/hyKruhRsEf/7QOG3FbTCL2KdOssuWgYP0/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/49993&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/49993&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/M6fbV/hyKrvVp3ad/5ZKiOE31tQC3ybu0Uiz840/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/FG2SP/hyKruhRsEf/7QOG3FbTCL2KdOssuWgYP0/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;&amp;nbsp;&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;p data-ke-size=&quot;size16&quot;&gt;그래도 조금 설명하자면&amp;nbsp;유저의 스킬 트리 중 선행 관련 스킬에 해당 되면 indexOf를 통해서 위치를 구하여서 선후를 구하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1622796662411&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로그래머스 스킬트리 문제
class SkillTree {
    public int solution(String skill, String[] skill_trees) {
        int answer = 0;
        for (String skillTree : skill_trees) {
            int learningIdx = 0;
            boolean isAble = true;
            for (char curSkill : skillTree.toCharArray()) {
                int skillIdx = skill.indexOf(curSkill);
                if (skillIdx == -1){
                    continue;
                }

                if (skillIdx != learningIdx) {
                    isAble = false;
                    break;
                }

                learningIdx++;
            }
            if (isAble)
                answer++;
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/213</guid>
      <comments>https://wellbell.tistory.com/213#entry213comment</comments>
      <pubDate>Fri, 4 Jun 2021 17:55:02 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 프렌즈4블록 문제 (자바)</title>
      <link>https://wellbell.tistory.com/212</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/17679&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/17679&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622795919612&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;코딩테스트 연습 - [1차] 프렌즈4블록&quot; data-og-description=&quot;프렌즈4블록 블라인드 공채를 통과한 신입 사원 라이언은 신규 게임 개발 업무를 맡게 되었다. 이번에 출시할 게임 제목은 &amp;quot;프렌즈4블록&amp;quot;. 같은 모양의 카카오프렌즈 블록이 2 &amp;times;2 형태로 4개가 붙&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17679&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17679&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjlVa4/hyKtgI3oEh/uwneUvkQCtjPporcK469u1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/blc5X1/hyKtgI3oB3/yvSii96YIo6RDl1yA1M6c0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/eciVmT/hyKruoD06r/3uThYt7p6UsytvoXQRgpqK/img.png?width=350&amp;amp;height=345&amp;amp;face=186_180_210_207&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/17679&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17679&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjlVa4/hyKtgI3oEh/uwneUvkQCtjPporcK469u1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/blc5X1/hyKtgI3oB3/yvSii96YIo6RDl1yA1M6c0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/eciVmT/hyKruoD06r/3uThYt7p6UsytvoXQRgpqK/img.png?width=350&amp;amp;height=345&amp;amp;face=186_180_210_207');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - [1차] 프렌즈4블록&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프렌즈4블록 블라인드 공채를 통과한 신입 사원 라이언은 신규 게임 개발 업무를 맡게 되었다. 이번에 출시할 게임 제목은 &quot;프렌즈4블록&quot;. 같은 모양의 카카오프렌즈 블록이 2 &amp;times;2 형태로 4개가 붙&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;모든 지점에서 아래, 오른쪽, 오른쪽 아래 3곳과 비교해서 전부 같으면 삭제하는 형태로 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. char 2차원 배열 -&amp;gt; int 2차원 배열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 모든 지점의 아래, 오른쪽, 오른쪽 아래 3곳 비교 후 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- 우선 int 2차원 배열을 깊은 복사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;(이유는 삭제 지점이 겹치는 경우 때문 비교에 사용하는 배열과 삭제를 반영하는 배열을 임시로 나누기 위함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- 지점별 3곳과 비교 후 전부 같으면 삭제 처리, 삭제 처리 과정에서 이미 삭제된 곳인 경우 count를 세지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;(저 같은 경우 삭제를 -1로 하여 처리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- 깊은 복사한 배열로 초기화 및 count 반영&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- count가 1이상이면 삭제 된 경우로 재배치를 하고 삭제 과정을 진행해야함으로 boolean값을 리턴하게 함&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;&amp;nbsp; &amp;nbsp;- 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;ps&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 배열은 clone 메서드, Arrays.copyOf 등으로 깊은 복사가 진행되지 않는다. 이유는 2차원 배열 내부에 int값이 있는게 아니라 int[] 객체이기 때문에 위의 메서드를 사용하더라고 객체를 참조하는 관계가 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 배열 깊은 복사는 직접 진행해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622795907394&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    // 아래, 오른쪽, 오른쪽 아래
    static int[] dx = {1, 0, 1};
    static int[] dy = {0, 1, 1};
    static int[][] matrix;
    static int answer;

    public int solution(int m, int n, String[] board) {
        matrix = new int[m][n];
        for(int i = 0; i &amp;lt; m; i++) {
            for (int j = 0; j &amp;lt; n; j++) {
                matrix[i][j] = board[i].charAt(j) - 'A';
            }
        }
        while (clear()) {
            move();
        }
        return answer;
    }

    static boolean clear() {
        int[][] temp = twoDimensionalArrayDeepCopy(matrix);
        int count = 0;

        for(int i = 0; i &amp;lt; matrix.length; i++) {
            for (int j = 0; j &amp;lt; matrix[i].length; j++) {
                boolean isWillRemove = true;

                for (int k = 0; k &amp;lt; 3; k++) {
                    int tx = i + dx[k];
                    int ty = j + dy[k];

                    if (!isRanged(tx, ty) || matrix[i][j] != matrix[tx][ty]) {
                        isWillRemove = false;
                        break;
                    }
                }

                if (isWillRemove) {
                    if (!isAlreadyRemove(temp[i][j])) {
                        temp[i][j] = -1;
                        count++;
                    }

                    for (int k = 0; k &amp;lt; 3; k++){
                        int tx = i + dx[k];
                        int ty = j + dy[k];
                        if (!isAlreadyRemove(temp[tx][ty])) {
                            temp[tx][ty] = -1;
                            count++;
                        }
                    }
                }
            }
        }
        matrix = temp;
        answer += count;
        return count &amp;gt; 0;
    }

    static void move() {
        for (int i = matrix.length - 1; i &amp;gt;= 0; i--) {
            for (int j = matrix[i].length - 1; j &amp;gt;= 0; j--) {
                if (matrix[i][j] == -1) {
                    int x = i;
                    while (x &amp;gt; 0 &amp;amp;&amp;amp; matrix[x][j] == -1) {
                        x--;
                    }
                    matrix[i][j] = matrix[x][j];
                    matrix[x][j] = -1;
                }
            }
        }
    }

    static int[][] twoDimensionalArrayDeepCopy(int[][] arr) {
        int[][] temp = new int[arr.length][arr[0].length];
        for (int i = 0; i &amp;lt; arr.length; i++) {
            for(int j = 0 ; j &amp;lt; arr[i].length; j++) {
                temp[i][j] = arr[i][j];
            }
        }
        return temp;
    }

    static boolean isAlreadyRemove(int val) {
        return val == -1;
    }

    static boolean isRanged(int x, int y) {
        return x &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; matrix.length &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;lt; matrix[0].length;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/212</guid>
      <comments>https://wellbell.tistory.com/212#entry212comment</comments>
      <pubDate>Fri, 4 Jun 2021 17:50:04 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - [3차] 파일명 정렬 문제 (자바)</title>
      <link>https://wellbell.tistory.com/211</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/17686&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/17686&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622726752422&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;코딩테스트 연습 - [3차] 파일명 정렬&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/17686&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17686&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Tf3NG/hyKrFb5EUE/Cc9dE6KcEQAAcaHKg6jJy0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/e0ZIJ/hyKrCTYF1R/3Z01pKhOht4DhlQgnXLJH0/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/17686&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/17686&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Tf3NG/hyKrFb5EUE/Cc9dE6KcEQAAcaHKg6jJy0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/e0ZIJ/hyKrCTYF1R/3Z01pKhOht4DhlQgnXLJH0/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;코딩테스트 연습 - [3차] 파일명 정렬&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;파일 이름을 head, number, tail로 분리 하고 comparable을 상속 받아서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;head가 다른 경우 head로 비교 정렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;head가 같은 경우 number를 숫자로 변환하여 비교 정렬&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;ps&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;equalsIgnoreCase(), compareToIgnoreCase()을 사용해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1622726780575&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.Collections;
import java.util.List;

// 프로그래머스 파일명 정렬 문제
class FileName implements Comparable&amp;lt;FileName&amp;gt;{
    String head;
    String number;
    String tail;

    public String getFileName() {
        return head + number + tail;
    }

    public FileName(String name) {
        StringBuilder headBuilder = new StringBuilder();
        char[] chars = name.toCharArray();
        int idx = 0;
        while(idx &amp;lt; chars.length &amp;amp;&amp;amp; (chars[idx] - '0' &amp;lt; 0 || chars[idx] - '0' &amp;gt; 9)) {
            headBuilder.append(chars[idx]);
            idx++;
        }
        head = headBuilder.toString();


        StringBuilder numberBuilder = new StringBuilder();
        while(idx &amp;lt; chars.length &amp;amp;&amp;amp; chars[idx] - '0' &amp;gt;= 0 &amp;amp;&amp;amp; chars[idx] - '0' &amp;lt;= 9) {
            numberBuilder.append(chars[idx]);
            idx++;
        }
        number = numberBuilder.toString();
        tail = name.substring(idx);
    }

    @Override
    public int compareTo(FileName o) {
        if(!head.equalsIgnoreCase(o.head)) {
            return head.compareToIgnoreCase(o.head);
        }
        return Integer.compare(Integer.parseInt(number), Integer.parseInt(o.number));
    }
}

class Solution {
    public String[] solution(String[] files) {
        List&amp;lt;FileName&amp;gt; fileNames = new ArrayList&amp;lt;&amp;gt;();
        String[] answer = new String[files.length];
        for (String fileName : files) {
            fileNames.add(new FileName(fileName));
        }

        Collections.sort(fileNames);

        for(int i = 0; i &amp;lt; answer.length; i++) {
            answer[i] = fileNames.get(i).getFileName();
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/211</guid>
      <comments>https://wellbell.tistory.com/211#entry211comment</comments>
      <pubDate>Thu, 3 Jun 2021 22:31:45 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - JadenCase 문자열 만들기 문제 (자바)</title>
      <link>https://wellbell.tistory.com/210</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12951&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12951&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622723718224&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;코딩테스트 연습 - JadenCase 문자열 만들기&quot; data-og-description=&quot;JadenCase란 모든 단어의 첫 문자가 대문자이고, 그 외의 알파벳은 소문자인 문자열입니다. 문자열 s가 주어졌을 때, s를 JadenCase로 바꾼 문자열을 리턴하는 함수, solution을 완성해주세요. 제한 조건 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12951&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12951&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b9rXll/hyKrCl5VUj/6yOcE3KucllFk3nteXDvD1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/cOfmIY/hyKrDL4K8y/17KamNrt87gwhX2rRjWGiK/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/12951&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12951&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b9rXll/hyKrCl5VUj/6yOcE3KucllFk3nteXDvD1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/cOfmIY/hyKrDL4K8y/17KamNrt87gwhX2rRjWGiK/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;코딩테스트 연습 - JadenCase 문자열 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JadenCase란 모든 단어의 첫 문자가 대문자이고, 그 외의 알파벳은 소문자인 문자열입니다. 문자열 s가 주어졌을 때, s를 JadenCase로 바꾼 문자열을 리턴하는 함수, solution을 완성해주세요. 제한 조건&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;1. 공백이 여러개 겹쳐있는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시에서는 공백이 문자마다 하나씩있는데 테스트 케이스안에는 &quot;&amp;nbsp; &amp;nbsp;&quot; 이런식으로 여러개가 붙어있는 경우가 있는 듯하다. 그래서 처음에 split을 통해서 처리했을 때 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 공백 문자열을 다 살려야 한다.&lt;/p&gt;
&lt;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;암튼 해결 방식은 문자열 전체를 char[]로 만들어서 하나씩 check한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 앞의 문자가 공백이면 대문자로 아니면 소문자로 처리하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1622723675426&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로그래머스 JadenCase 문자열 만들기 문제
class Solution {
    public String solution(String s) {
        char[] chars = s.toCharArray();

        boolean isPreBlank  = true;
        StringBuilder answer = new StringBuilder();
        for (char aChar : chars) {
            if (aChar == ' ') {
                answer.append(' ');
                isPreBlank =true;
            }else {
                if (isPreBlank) {
                    isPreBlank = false;
                    answer.append(String.valueOf(aChar).toUpperCase());
                }else {
                    answer.append(String.valueOf(aChar).toLowerCase());
                }
            }
        }
        return answer.toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/210</guid>
      <comments>https://wellbell.tistory.com/210#entry210comment</comments>
      <pubDate>Thu, 3 Jun 2021 21:41:36 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 행렬의 곱셈 문제 (자바)</title>
      <link>https://wellbell.tistory.com/209</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12949&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12949&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1622721674516&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;[[2, 3, 2], [4, 2, 4], [3, 1, 4]] [[5, 4, 3], [2, 4, 1], [3, 1, 1]] [[22, 22, 11], [36, 28, 18], [29, 20, 14]]&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12949&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12949&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEYZVe/hyKrBm707U/kxKKVayvhlmFSP4fHeJNKk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bFYeCv/hyKrBtUDSw/KV2qxM1ewegFYrL6t1gc80/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/12949&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12949&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEYZVe/hyKrBm707U/kxKKVayvhlmFSP4fHeJNKk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bFYeCv/hyKrBtUDSw/KV2qxM1ewegFYrL6t1gc80/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;[[2, 3, 2], [4, 2, 4], [3, 1, 4]] [[5, 4, 3], [2, 4, 1], [3, 1, 1]] [[22, 22, 11], [36, 28, 18], [29, 20, 14]]&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;행렬의 곱 계산 방식을 알면 간단하게 풀 수 있는 문제로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 설명하자면 선 행렬의 행과 후 행렬의 열을 제대로 매칭하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후로는 말로 설명하는 것보다 코드로 보는 것이 좋을 것 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1622721228721&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int[][] solution(int[][] arr1, int[][] arr2) {
        int fLen = arr1.length;
        int sLen = arr2[0].length;
        int colLen = arr2.length;

        int[][] answer = new int[fLen][sLen];
        for (int i = 0; i &amp;lt; fLen; i++) {
            for (int j  = 0; j &amp;lt; sLen; j++) {
                for (int k = 0; k &amp;lt; colLen; k++) {
                    answer[i][j] += arr1[i][k] * arr2[k][j];
                }
            }
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/209</guid>
      <comments>https://wellbell.tistory.com/209#entry209comment</comments>
      <pubDate>Thu, 3 Jun 2021 21:01:18 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 이진 변환 반복하기 문제 (자바)</title>
      <link>https://wellbell.tistory.com/208</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/70129&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/70129&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622648685646&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/70129&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/70129&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OzJFL/hyKrHtqf73/xHVWwxBNgINkknwXOKxjL0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/iuoH5/hyKrunjcJY/NL8Ta6ZgW21TpF2kJhQvw0/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/70129&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/70129&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OzJFL/hyKrHtqf73/xHVWwxBNgINkknwXOKxjL0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/iuoH5/hyKrunjcJY/NL8Ta6ZgW21TpF2kJhQvw0/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;&amp;nbsp;&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;p data-ke-size=&quot;size16&quot;&gt;replace 이후&amp;nbsp; 길이를 통해서 제거된 0의 개수 정도입니다.&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;ps&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replace와 replaceAll의 차이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String replace(CharSequnce target, CharSequence replacement) - 특정 문자를 특정 문자로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String replaceAll(String regex, String replacement) - 정규식에 해당 되는 경우 특정 문자열로 변경&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ex)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;String str = &quot;aaabbbccccabcddddabcdeeee&quot;;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;String result1 = str.replace(&quot;abc&quot;, &quot;왕&quot;); &lt;span style=&quot;color: #666666;&quot;&gt;-&amp;gt; aaabbbcccc왕dddd왕deeee&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;String result2 = str.replaceAll(&quot;[abc]&quot;, &quot;왕&quot;); -&lt;span style=&quot;color: #666666;&quot;&gt;&amp;gt;왕왕왕왕왕왕왕왕왕왕왕왕왕dddd왕왕왕deeee&lt;/span&gt;&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_1622648649783&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로그래머스 이진 변환 반복하기 문제
class Solution {
    public int[] solution(String s) {
        int[] answer = new int[2];
        while (!s.equals(&quot;1&quot;)) {
            int oLen = s.length();
            s = s.replace(&quot;0&quot;, &quot;&quot;);
            int nLen = s.length();
            answer[0] += oLen - nLen;

            s = Integer.toBinaryString(nLen);
            answer[1] += 1;
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/208</guid>
      <comments>https://wellbell.tistory.com/208#entry208comment</comments>
      <pubDate>Thu, 3 Jun 2021 01:00:46 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 피보나치 수 문제 (자바)</title>
      <link>https://wellbell.tistory.com/207</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12945&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12945&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622647902502&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;피보나치 수는 F(0) = 0, F(1) = 1일 때, 1 이상의 n에 대하여 F(n) = F(n-1) + F(n-2) 가 적용되는 수 입니다. 예를들어 F(2) = F(0) + F(1) = 0 + 1 = 1 F(3) = F(1) + F(2) = 1 + 1 = 2 F(4) = F(2) + F(3) = 1 + 2 = 3 F(5) = F(3) + F(4) = &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12945&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12945&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b7YuyH/hyKqMpyAYk/6Ia9fwAhCmlBmQpLaLIW9K/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bwedwV/hyKrzB8Ncr/5G34k97dveoowUyaI8oZA1/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/12945&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12945&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b7YuyH/hyKqMpyAYk/6Ia9fwAhCmlBmQpLaLIW9K/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bwedwV/hyKrzB8Ncr/5G34k97dveoowUyaI8oZA1/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;피보나치 수는 F(0) = 0, F(1) = 1일 때, 1 이상의 n에 대하여 F(n) = F(n-1) + F(n-2) 가 적용되는 수 입니다. 예를들어 F(2) = F(0) + F(1) = 0 + 1 = 1 F(3) = F(1) + F(2) = 1 + 1 = 2 F(4) = F(2) + F(3) = 1 + 2 = 3 F(5) = F(3) + F(4) =&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;p data-ke-size=&quot;size16&quot;&gt;조심해야하는 부분은 숫자의 범위인데 n이 100,000 까지 가능해서 long의 범위를 뛰어 넘게 됩니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 메모제이션할때 부터 나눈 나머지로 셋팅하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1622647885518&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public static int[] dp;
    public int solution(int n) {
        dp = new int[n + 1];
        return fibo(n) % 1234567;
    }

    public static int fibo(int n) {
        if (n == 0) {
            return 0;
        }

        if (n &amp;lt;= 2) {
            return 1;
        }

        if(dp[n] != 0) {
            return dp[n];
        }

        return dp[n] = (fibo(n - 1) + fibo(n - 2)) % 1234567;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/207</guid>
      <comments>https://wellbell.tistory.com/207#entry207comment</comments>
      <pubDate>Thu, 3 Jun 2021 00:33:43 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 우유와 요거트가 담긴 장바구니 (SQL)</title>
      <link>https://wellbell.tistory.com/206</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/62284&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/62284&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622645688792&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;CART_PRODUCTS 테이블은 장바구니에 담긴 상품 정보를 담은 테이블입니다. CART_PRODUCTS 테이블의 구조는 다음과 같으며, ID, CART_ID, NAME, PRICE는 각각 테이블의 아이디, 장바구니의 아이디, 상품 종류, 가&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/62284&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/62284&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6mPkS/hyKqOHEYT4/H5gpPL30bA124EE1TprNwK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/E9FMu/hyKrDLiLmn/OcCd1CiUHGc8NsIEQku3P0/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/62284&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/62284&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6mPkS/hyKqOHEYT4/H5gpPL30bA124EE1TprNwK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/E9FMu/hyKrDLiLmn/OcCd1CiUHGc8NsIEQku3P0/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;CART_PRODUCTS 테이블은 장바구니에 담긴 상품 정보를 담은 테이블입니다. CART_PRODUCTS 테이블의 구조는 다음과 같으며, ID, CART_ID, NAME, PRICE는 각각 테이블의 아이디, 장바구니의 아이디, 상품 종류, 가&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;3가지 방식으로 풀 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 문제들에서 전체 체크하는 IN절보다 EXISTS가 좋을 수 있다고 말해왔는데&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번째 방식의 문제는 서브쿼리를 2개 사용하면서 임시 테이블을 2개 사용하게 된다는 점인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브쿼리 최적화가 좋지 못한 DB나 버전에서 좋지 않을 수 있습니다.&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;이유는 조인 쿼리의 경우 2개의 임시테이블을 만들고 NL조인을 하게 되는데 이 경우 각각 임시 테이블이 조건에 맞는 테이블로 걸러져서 만들어지기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조인대상이 되는 두 테이블의 row 수가 확실하게 작아 질 수 있기 때문에 cost가 작아질 수 있습니다.&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;이처럼 데이터가 많냐 적냐, 버전, DB종류, 카디널리티 높냐 낮냐 등 많은 것에 의해서 적절한 쿼리문 달라질 수 있습니다.&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;1. IN 절&lt;/p&gt;
&lt;pre id=&quot;code_1622644466677&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT CART_ID
FROM CART_PRODUCTS
WHERE NAME = 'Yogurt'
AND CART_ID IN (SELECT CART_ID FROM CART_PRODUCTS WHERE NAME = 'Milk')
ORDER BY CART_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;162&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crM6Lm/btq6q1yUZpq/5nS4Kfa1RUEYoyU5w6ikO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crM6Lm/btq6q1yUZpq/5nS4Kfa1RUEYoyU5w6ikO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crM6Lm/btq6q1yUZpq/5nS4Kfa1RUEYoyU5w6ikO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrM6Lm%2Fbtq6q1yUZpq%2F5nS4Kfa1RUEYoyU5w6ikO1%2Fimg.png&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;162&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. EXISTS&lt;/p&gt;
&lt;pre id=&quot;code_1622644798526&quot; class=&quot;sql&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT CART_ID
FROM CART_PRODUCTS CP1
WHERE 
    NAME = 'Yogurt'
AND 
    EXISTS(
        SELECT 1
        FROM CART_PRODUCTS CP2
        WHERE
            CP1.CART_ID = CP2.CART_ID
        AND 
            NAME = 'Milk')
ORDER BY CART_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1399&quot; data-origin-height=&quot;172&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/19dvH/btq6qSPtOqg/pQ8bplaPptQDpa6knGCQMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/19dvH/btq6qSPtOqg/pQ8bplaPptQDpa6knGCQMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/19dvH/btq6qSPtOqg/pQ8bplaPptQDpa6knGCQMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F19dvH%2Fbtq6qSPtOqg%2FpQ8bplaPptQDpa6knGCQMK%2Fimg.png&quot; data-origin-width=&quot;1399&quot; data-origin-height=&quot;172&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;3.&amp;nbsp; 조인&lt;/p&gt;
&lt;pre id=&quot;code_1622644763361&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT CP1.CART_ID
FROM
    (SELECT CART_ID FROM CART_PRODUCTS WHERE NAME = 'Milk') CP1,
    (select CART_ID FROM CART_PRODUCTS WHERE NAME = 'Yogurt') CP2
where CP1.CART_ID = CP2.CART_ID
ORDER BY CP1.CART_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1379&quot; data-origin-height=&quot;122&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwTBqu/btq6pI7URm3/GSe5E7UbE1EFB7STKkkO60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwTBqu/btq6pI7URm3/GSe5E7UbE1EFB7STKkkO60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwTBqu/btq6pI7URm3/GSe5E7UbE1EFB7STKkkO60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwTBqu%2Fbtq6pI7URm3%2FGSe5E7UbE1EFB7STKkkO60%2Fimg.png&quot; data-origin-width=&quot;1379&quot; data-origin-height=&quot;122&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;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/206</guid>
      <comments>https://wellbell.tistory.com/206#entry206comment</comments>
      <pubDate>Wed, 2 Jun 2021 23:55:21 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 오랜 기간 보호한 동물(1) (SQL)</title>
      <link>https://wellbell.tistory.com/204</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/59044&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/59044&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622526336776&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;코딩테스트 연습 - 오랜 기간 보호한 동물(1)&quot; data-og-description=&quot;ANIMAL_INS 테이블은 동물 보호소에 들어온 동물의 정보를 담은 테이블입니다. ANIMAL_INS 테이블 구조는 다음과 같으며, ANIMAL_ID, ANIMAL_TYPE, DATETIME, INTAKE_CONDITION, NAME, SEX_UPON_INTAKE는 각각 동물의 아이디&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/59044&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/59044&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uDKDv/hyKqSaMnYZ/fIBfqkJLewohGHWWj56aQK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/myPo7/hyKoYKs2VZ/8uPsY75LEgXcSzYagonzD0/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/59044&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/59044&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uDKDv/hyKqSaMnYZ/fIBfqkJLewohGHWWj56aQK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/myPo7/hyKoYKs2VZ/8uPsY75LEgXcSzYagonzD0/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;코딩테스트 연습 - 오랜 기간 보호한 동물(1)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ANIMAL_INS 테이블은 동물 보호소에 들어온 동물의 정보를 담은 테이블입니다. ANIMAL_INS 테이블 구조는 다음과 같으며, ANIMAL_ID, ANIMAL_TYPE, DATETIME, INTAKE_CONDITION, NAME, SEX_UPON_INTAKE는 각각 동물의 아이디&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;NOT EXISTS, NOT IN, LEFT JOIN을 사용하여 풀어 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 성능 차이는 데이터량, db 종류 등에 따라 달라질 수 있을 거 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예로 서브쿼리 최적화가 좋지 못한 DB라면&amp;nbsp; LEFT JOIN을 사용해야하겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브쿼리 성능관련해서는 &lt;a href=&quot;https://jojoldu.tistory.com/520&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jojoldu.tistory.com/520&lt;/a&gt;를 참조하시면 좋을거 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NOT EXISTS, NOT IN 두 가지를 사용할때 주의할 점은 null을 처리하는 방법이 다르다는 것입니다.&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. NOT IN 절은 AND 절로 모두 IN절 내의 모든 값에 포함되지 않아야 하는데 NULL의 경우 이를 확인할수 없는 값으로 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Not In 은 Where 절의 조건이 만족하더라도 연결고리 컬럼이 Null값을 갖는 경우 결과에서 제외&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Not Exists는 Not In과 달리 Null값을 가진 Row들도 결과에 포함됩니다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이는 연결고리 값이 Null값을 가질때 Not In은 조인 연산을 하지 않기 때문에 결과에서 제외되며&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Not&amp;nbsp;Exists는&amp;nbsp;Exists&amp;nbsp;이하의&amp;nbsp;절이&amp;nbsp;False를&amp;nbsp;리턴하고&amp;nbsp;거기에&amp;nbsp;대한&amp;nbsp;Not이기&amp;nbsp;때문에&amp;nbsp;결과적으로&amp;nbsp;true가&amp;nbsp;되어&amp;nbsp;결과에&amp;nbsp;포함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. NOT EXISTS&lt;/p&gt;
&lt;pre id=&quot;code_1622524898686&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT NAME, DATETIME
FROM ANIMAL_INS AI
WHERE NOT EXISTS(SELECT 1 FROM ANIMAL_OUTS AO WHERE AI.ANIMAL_ID = AO.ANIMAL_ID)
ORDER BY DATETIME 
LIMIT 0, 3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1105&quot; data-origin-height=&quot;190&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNn28K/btq6kFCBwwr/uO2UZWuf6O46zMkTob5BeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNn28K/btq6kFCBwwr/uO2UZWuf6O46zMkTob5BeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNn28K/btq6kFCBwwr/uO2UZWuf6O46zMkTob5BeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNn28K%2Fbtq6kFCBwwr%2FuO2UZWuf6O46zMkTob5BeK%2Fimg.png&quot; data-origin-width=&quot;1105&quot; data-origin-height=&quot;190&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;2. NOT IN&lt;/p&gt;
&lt;pre id=&quot;code_1622524991401&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT NAME, DATETIME
FROM ANIMAL_INS
WHERE ANIMAL_ID NOT IN(SELECT ANIMAL_ID FROM ANIMAL_OUTS)
ORDER BY DATETIME 
LIMIT 0, 3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;154&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crS8yi/btq6kNgkWMK/Wk6b7hEx6wsqgh9Iqq6C8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crS8yi/btq6kNgkWMK/Wk6b7hEx6wsqgh9Iqq6C8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crS8yi/btq6kNgkWMK/Wk6b7hEx6wsqgh9Iqq6C8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrS8yi%2Fbtq6kNgkWMK%2FWk6b7hEx6wsqgh9Iqq6C8K%2Fimg.png&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;154&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;3.LEFT JOIN&lt;/p&gt;
&lt;pre id=&quot;code_1622525209446&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT AI.NAME, AI.DATETIME
FROM ANIMAL_INS AI LEFT JOIN ANIMAL_OUTS AO
ON AI.ANIMAL_ID = AO.ANIMAL_ID
WHERE AO.ANIMAL_ID IS NULL
ORDER BY AI.DATETIME
LIMIT 0, 3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;125&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMs5Rb/btq6fT3cb6g/Hhy2V79wFfz34PmtIT9wP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMs5Rb/btq6fT3cb6g/Hhy2V79wFfz34PmtIT9wP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMs5Rb/btq6fT3cb6g/Hhy2V79wFfz34PmtIT9wP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMs5Rb%2Fbtq6fT3cb6g%2FHhy2V79wFfz34PmtIT9wP1%2Fimg.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;125&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;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/204</guid>
      <comments>https://wellbell.tistory.com/204#entry204comment</comments>
      <pubDate>Tue, 1 Jun 2021 14:45:59 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 숫자의 표현 문제 (자바)</title>
      <link>https://wellbell.tistory.com/203</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12924&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12924&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622524069567&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;Finn은 요즘 수학공부에 빠져 있습니다. 수학 공부를 하던 Finn은 자연수 n을 연속한 자연수들로 표현 하는 방법이 여러개라는 사실을 알게 되었습니다. 예를들어 15는 다음과 같이 4가지로 표현 할&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12924&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12924&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hTA53/hyKqS2UR5K/12z1CZYDVWBanKiGYxKzSK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/dvB3dM/hyKoXY4Xru/lhqvdoBkX03jOFGA5rpgOk/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/12924&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12924&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hTA53/hyKqS2UR5K/12z1CZYDVWBanKiGYxKzSK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/dvB3dM/hyKoXY4Xru/lhqvdoBkX03jOFGA5rpgOk/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;Finn은 요즘 수학공부에 빠져 있습니다. 수학 공부를 하던 Finn은 자연수 n을 연속한 자연수들로 표현 하는 방법이 여러개라는 사실을 알게 되었습니다. 예를들어 15는 다음과 같이 4가지로 표현 할&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;n이 10000 이하이기 때문에 전체 계산을 해도 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부터 더해가면서 n과 비교해나아가면서 적절이 break문을 사용하면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1622524042995&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로그래머스 숫자의 표현 문제
class Solution {
    public int solution(int n) {
        int answer = 0;
        for (int i = 1; i &amp;lt;= n; i++) {
            int sum = 0;
            for(int j = i; j &amp;lt;= n; j++) {
                sum += j;
                if (sum == n) {
                    answer++;
                    break;
                }else if (sum &amp;gt; n){
                    break;
                }
            }
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/203</guid>
      <comments>https://wellbell.tistory.com/203#entry203comment</comments>
      <pubDate>Tue, 1 Jun 2021 14:15:56 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 2개 이하로 다른 비트 문제 (자바)</title>
      <link>https://wellbell.tistory.com/202</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/77885&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/77885&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622521775189&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;코딩테스트 연습 - 2개 이하로 다른 비트&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/77885&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/77885&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yH3om/hyKo3ZfHAT/cX2kyzYIJ8pVDV7FeyHENk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/l0BJE/hyKqJrlNn3/FsPc0vDY1IVJPYTSaixSqK/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/77885&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/77885&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yH3om/hyKo3ZfHAT/cX2kyzYIJ8pVDV7FeyHENk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/l0BJE/hyKqJrlNn3/FsPc0vDY1IVJPYTSaixSqK/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;코딩테스트 연습 - 2개 이하로 다른 비트&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;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;p data-ke-size=&quot;size16&quot;&gt;기본적으로 짝수 홀수부터 나눠야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짝수면 + 1만 진행 하면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 짝수의 2진수는 모두 0으로 끝나기 때문에 끝을 1로 바꾸는 것 만으로 해결이 가능하기 때문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10 -&amp;gt; 11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100 -&amp;gt; 101&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;0을 미포함하는 경우 즉 1로만 이루어진 경우 앞에서 2번째 자리에 0을 삽입하여 해결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 111 -&amp;gt; 1011&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;0을 포함하는 경우 마지막 0을 1로 바꾸고 그 뒤에 1을 0으로 바꾸어서 해결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 10011 -&amp;gt; 10101&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622521765909&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로그래머스 2개 이하로 다른 비트 문제
class Solution {
    public long[] solution(long[] numbers) {
        long[] answer = new long[numbers.length];
        for (int i = 0; i &amp;lt; numbers.length; i++) {
            if (numbers[i] % 2 == 0) {
                answer[i] = numbers[i] + 1;

            } else {
                StringBuilder temp = new StringBuilder();
                String binaryString = Long.toBinaryString(numbers[i]);
                if (!binaryString.contains(&quot;0&quot;)) {
                    // 맨 앞을 10으로
                    temp.append(&quot;10&quot;);

                    // 나머지 자리수를 모두 1로
                    temp.append(binaryString.substring(1).replace(&quot;0&quot;, &quot;1&quot;));

                } else {
                    int lastIndex = binaryString.lastIndexOf(&quot;0&quot;);
                    int firstOneIndex = binaryString.indexOf(&quot;1&quot;, lastIndex);
                    // 마지막 0을 1로 수정
                    temp.append(binaryString, 0, lastIndex).append(&quot;1&quot;);

                    // 마지막 0 다음 1을 0으로 수정
                    temp.append(&quot;0&quot;);

                    // 그 다음 비트열
                    temp.append(binaryString.substring(firstOneIndex + 1));
                }
                answer[i] = Long.parseLong(temp.toString(), 2);
            }
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/202</guid>
      <comments>https://wellbell.tistory.com/202#entry202comment</comments>
      <pubDate>Tue, 1 Jun 2021 13:33:29 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 삼각 달팽이 문제 (자바)</title>
      <link>https://wellbell.tistory.com/201</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/68645&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/68645&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622469380136&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;5 [1,2,12,3,13,11,4,14,15,10,5,6,7,8,9] 6 [1,2,15,3,16,14,4,17,21,13,5,18,19,20,12,6,7,8,9,10,11]&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/68645&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/68645&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bc22Gy/hyKqM2kvE6/YUek21aHGQik9OWtxgIxdK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/AXvBw/hyKoPNavLw/35Wpkcwnk6xMj0qaelt5o1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/AMEpi/hyKqHmpY1Q/iagjufjbKkwMiGKWFXFNTK/img.png?width=1189&amp;amp;height=525&amp;amp;face=0_0_1189_525&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/68645&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/68645&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bc22Gy/hyKqM2kvE6/YUek21aHGQik9OWtxgIxdK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/AXvBw/hyKoPNavLw/35Wpkcwnk6xMj0qaelt5o1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/AMEpi/hyKqHmpY1Q/iagjufjbKkwMiGKWFXFNTK/img.png?width=1189&amp;amp;height=525&amp;amp;face=0_0_1189_525');&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;5 [1,2,12,3,13,11,4,14,15,10,5,6,7,8,9] 6 [1,2,15,3,16,14,4,17,21,13,5,18,19,20,12,6,7,8,9,10,11]&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;155&quot; data-origin-height=&quot;140&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chcJ43/btq59Ono24x/oSRCHjbgzTZZVTBMehaXk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chcJ43/btq59Ono24x/oSRCHjbgzTZZVTBMehaXk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chcJ43/btq59Ono24x/oSRCHjbgzTZZVTBMehaXk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchcJ43%2Fbtq59Ono24x%2FoSRCHjbgzTZZVTBMehaXk1%2Fimg.png&quot; data-origin-width=&quot;155&quot; data-origin-height=&quot;140&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;1. 왼쪽 위에서 아래로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 왼쪽 아래에서 오른쪽 아래로&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 3개의 반복으로 값을 채워가면 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622469544418&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로그래머스 삼각 달팽이 문제
class Solution {
    public int[] solution(int n) {
        int max = n * (n + 1) / 2;
        int[][] matrix = new int[n][n];
        int[] answer = new int[max];

        // 시작 지점 초기화
        int x = 0, y = 0;
        int value = 1;
        matrix[0][0] = 1;

        while (value &amp;lt; max) {
            // 왼쪽 - 위에서 아래로
            while (x + 1 &amp;lt; n &amp;amp;&amp;amp; matrix[x + 1][y] == 0) {
                matrix[++x][y] = ++value;
            }

            // 아래 - 왼쪽에서 오른쪽으로
            while (y + 1 &amp;lt; n &amp;amp;&amp;amp; matrix[x][y + 1] == 0) {
                matrix[x][++y] = ++value;
            }

            // 오른쪽 아래에서 대각선 위로
            while (y - 1 &amp;gt; 0 &amp;amp;&amp;amp; x - 1 &amp;gt; 0 &amp;amp;&amp;amp; matrix[x - 1][y - 1] == 0) {
                matrix[--x][--y] = ++value;
            }
        }
        int idx = 0;
        for(int i = 0; i &amp;lt; matrix.length; i++) {
            for (int j = 0; j &amp;lt;= i; j++) {
                answer[idx] = matrix[i][j];
                idx++;
            }
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 테스트/프로그래머스</category>
      <author>wellbell</author>
      <guid isPermaLink="true">https://wellbell.tistory.com/201</guid>
      <comments>https://wellbell.tistory.com/201#entry201comment</comments>
      <pubDate>Mon, 31 May 2021 23:02:38 +0900</pubDate>
    </item>
  </channel>
</rss>