<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>kimkata's Devlog</title>
    <link>https://katastrophe.tistory.com/</link>
    <description>은탄이 아닌 레포지토리</description>
    <language>ko</language>
    <pubDate>Sat, 30 May 2026 12:56:34 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>김까따</managingEditor>
    <image>
      <title>kimkata's Devlog</title>
      <url>https://tistory1.daumcdn.net/tistory/4780186/attach/a01df58f944046b6a4219e1d39d3360f</url>
      <link>https://katastrophe.tistory.com</link>
    </image>
    <item>
      <title>한 입 크기로 잘라먹는 Next 강의 이벤트!</title>
      <link>https://katastrophe.tistory.com/193</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2858&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zjrbf/btsI1VgwAm7/zcEbHqCw6xhBNxqehw2IDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zjrbf/btsI1VgwAm7/zcEbHqCw6xhBNxqehw2IDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zjrbf/btsI1VgwAm7/zcEbHqCw6xhBNxqehw2IDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZjrbf%2FbtsI1VgwAm7%2FzcEbHqCw6xhBNxqehw2IDK%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;2858&quot; height=&quot;696&quot; data-origin-width=&quot;2858&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이전에 한 입 크기로 잘라먹는 리액트 강의 쿠폰을 받아 샀었다가 볼 일이 있겠거니 하고 묻어뒀던 강의 ..&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최근에 이직을 하고 프론트까지 개발해야 하다보니 허겁지겁 인프런 강의를 꺼내봤다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링 백엔드 개발자로서 프론트는 완전 처음이라 갈피를 못 잡았었는데 이정환님의 한입크기 강의가 굉장히 도움이 됐었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이번에 Next.js 로 프로젝트를 진행해야 하는 상황이 왔는데 마침 정환님께서 또 강의를 출시 예정이라고 한다..!!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.winterlood.com/post/%EC%82%AC%EC%A0%84%20%EB%93%B1%EB%A1%9D%20%EC%9D%B4%EB%B2%A4%ED%8A%B8%20-%20%ED%95%9C%20%EC%9E%85%20%ED%81%AC%EA%B8%B0%EB%A1%9C%20%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94%20Nextjs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.winterlood.com/post/%EC%82%AC%EC%A0%84%20%EB%93%B1%EB%A1%9D%20%EC%9D%B4%EB%B2%A4%ED%8A%B8%20-%20%ED%95%9C%20%EC%9E%85%20%ED%81%AC%EA%B8%B0%EB%A1%9C%20%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94%20Nextjs&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723437112315&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;사전등록 이벤트 - 한 입 크기로 잘라먹는 Next.js(15+) - Winterlood&quot; data-og-description=&quot;사전등록 이벤트 - 한 입 크기로 잘라먹는 Next.js(15+) - 신청자 전원 50% 할인 쿠폰 증정  &amp;ldquo;한 입 크기로 잘라먹는 Nextjs&amp;rdquo; 사전 등록 이벤트&quot; data-og-host=&quot;www.winterlood.com&quot; data-og-source-url=&quot;https://www.winterlood.com/post/%EC%82%AC%EC%A0%84%20%EB%93%B1%EB%A1%9D%20%EC%9D%B4%EB%B2%A4%ED%8A%B8%20-%20%ED%95%9C%20%EC%9E%85%20%ED%81%AC%EA%B8%B0%EB%A1%9C%20%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94%20Nextjs&quot; data-og-url=&quot;https://www.winterlood.com/post/%EC%82%AC%EC%A0%84%20%EB%93%B1%EB%A1%9D%20%EC%9D%B4%EB%B2%A4%ED%8A%B8%20-%20%ED%95%9C%20%EC%9E%85%20%ED%81%AC%EA%B8%B0%EB%A1%9C%20%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94%20Nextjs&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8LcZz/hyWOcaDvY7/V3eJzHPdDYdl8647f9EYEK/img.jpg?width=1216&amp;amp;height=781&amp;amp;face=843_171_1031_376,https://scrap.kakaocdn.net/dn/dOOuuT/hyWKBwnEBL/kTJkJVbMTD2Tsfzpkox9CK/img.jpg?width=1216&amp;amp;height=781&amp;amp;face=843_171_1031_376,https://scrap.kakaocdn.net/dn/ngngW/hyWOiaQkQg/vpOQ69QSfUhpBfYMSeHL20/img.png?width=800&amp;amp;height=513&amp;amp;face=561_116_667_232&quot;&gt;&lt;a href=&quot;https://www.winterlood.com/post/%EC%82%AC%EC%A0%84%20%EB%93%B1%EB%A1%9D%20%EC%9D%B4%EB%B2%A4%ED%8A%B8%20-%20%ED%95%9C%20%EC%9E%85%20%ED%81%AC%EA%B8%B0%EB%A1%9C%20%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94%20Nextjs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.winterlood.com/post/%EC%82%AC%EC%A0%84%20%EB%93%B1%EB%A1%9D%20%EC%9D%B4%EB%B2%A4%ED%8A%B8%20-%20%ED%95%9C%20%EC%9E%85%20%ED%81%AC%EA%B8%B0%EB%A1%9C%20%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94%20Nextjs&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8LcZz/hyWOcaDvY7/V3eJzHPdDYdl8647f9EYEK/img.jpg?width=1216&amp;amp;height=781&amp;amp;face=843_171_1031_376,https://scrap.kakaocdn.net/dn/dOOuuT/hyWKBwnEBL/kTJkJVbMTD2Tsfzpkox9CK/img.jpg?width=1216&amp;amp;height=781&amp;amp;face=843_171_1031_376,https://scrap.kakaocdn.net/dn/ngngW/hyWOiaQkQg/vpOQ69QSfUhpBfYMSeHL20/img.png?width=800&amp;amp;height=513&amp;amp;face=561_116_667_232');&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;사전등록 이벤트 - 한 입 크기로 잘라먹는 Next.js(15+) - Winterlood&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;사전등록 이벤트 - 한 입 크기로 잘라먹는 Next.js(15+) - 신청자 전원 50% 할인 쿠폰 증정  &amp;ldquo;한 입 크기로 잘라먹는 Nextjs&amp;rdquo; 사전 등록 이벤트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.winterlood.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;사전등록 이벤트로 50% 할인에 추첨으로 100% 할인 쿠폰까지 준다고하니 많관부..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #000000; color: #ffffff; text-align: left;&quot;&gt;&lt;a href=&quot;https://smore.im/form/gIl1v0ftOn)&quot;&gt;https://smore.im/form/gIl1v0ftOn&lt;/a&gt;&lt;/span&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;2124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pSMZF/btsI7kaCn3d/1PZOvGjHI5yqS4yHkFoOUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pSMZF/btsI7kaCn3d/1PZOvGjHI5yqS4yHkFoOUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pSMZF/btsI7kaCn3d/1PZOvGjHI5yqS4yHkFoOUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpSMZF%2FbtsI7kaCn3d%2F1PZOvGjHI5yqS4yHkFoOUK%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;401&quot; height=&quot;789&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;2124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다 .. 감사합니다 ..&lt;/p&gt;</description>
      <category>etc/잡담</category>
      <category>nextjs</category>
      <category>이정환</category>
      <category>한입크기시리즈</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/193</guid>
      <comments>https://katastrophe.tistory.com/193#entry193comment</comments>
      <pubDate>Mon, 12 Aug 2024 13:33:28 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [21318] 피아노 체조 JAVA</title>
      <link>https://katastrophe.tistory.com/192</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/21318&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/21318&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drveNG/btsH9e2A850/lJpuoP5TBgwSFlJ1J4JvY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drveNG/btsH9e2A850/lJpuoP5TBgwSFlJ1J4JvY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drveNG/btsH9e2A850/lJpuoP5TBgwSFlJ1J4JvY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrveNG%2FbtsH9e2A850%2FlJpuoP5TBgwSFlJ1J4JvY0%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;996&quot; height=&quot;765&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;765&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1719066281417&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package programmers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import static java.util.Arrays.stream;

public class Main {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String[] args) throws IOException {

        int n = Integer.parseInt(br.readLine());
        int[] arr = stream((&quot;0 &quot;+br.readLine()).split(&quot; &quot;))
                .mapToInt(Integer::parseInt)
                .toArray();
        int[] dp = new int[n+1];
        arr[0] = arr[1];

        for(int i=1;i&amp;lt;=n;i++){

            if(arr[i] &amp;lt; arr[i-1]){ // i-1 번째 곡이 i 곡보다 더 어렵다면 i-1 번째에 실수 +1
                dp[i-1]++;
            }
            dp[i] = dp[i-1]; // i번째 기본값으로 이전 i-1 번째 실수 누적 합으로 넣기
        }
        int queryCount = Integer.parseInt(br.readLine());
        StringBuilder result = new StringBuilder();

        for(int i=0;i&amp;lt;queryCount;i++){

            int[] query = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(e -&amp;gt; Integer.parseInt(e)-1)
                    .toArray();
            // 전 구간 -1 한 이유
            // 마지막 곡에선 실수를 하지 않기때문에 마지막 곡 그 전 인덱스 까지만 본다.
            // 첫 곡 이전 실수 까지 가져와야하기 때문에 첫 곡 그 전 인덱스 까지만 본다.

            result.append(dp[query[1]] - dp[query[0]]+&quot;\n&quot;);
        }
        System.out.println(result);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>누적합</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/192</guid>
      <comments>https://katastrophe.tistory.com/192#entry192comment</comments>
      <pubDate>Sat, 22 Jun 2024 23:25:02 +0900</pubDate>
    </item>
    <item>
      <title>[AWS RDS] mysql: table doesn't exist</title>
      <link>https://katastrophe.tistory.com/191</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 문제상황&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Spring 백단에서 프로퍼티별 개발환경을 나누고 로컬에선 H2db, 개발서버에선 aws rds mysql 8.0 을 사용하도록 datasource를 연결하였다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그리고 h2db쪽에서는 쿼리가 잘 날라가는 걸 확인.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;테스트 완료후 ec2에 배포하고 개발서버에서 실행했는데 table이 없다고 나옴.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2270&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9Sqa/btsD5vBDd5E/o0INhqPSHSc5t81tsC2Oek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9Sqa/btsD5vBDd5E/o0INhqPSHSc5t81tsC2Oek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9Sqa/btsD5vBDd5E/o0INhqPSHSc5t81tsC2Oek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9Sqa%2FbtsD5vBDd5E%2Fo0INhqPSHSc5t81tsC2Oek%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;2270&quot; height=&quot;1014&quot; data-origin-width=&quot;2270&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 시도한 방법들&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; - JPA Naming Strategy&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AgUWr/btsD6yrsdaR/SCzut2fVP7gRjLVxnenYNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AgUWr/btsD6yrsdaR/SCzut2fVP7gRjLVxnenYNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AgUWr/btsD6yrsdaR/SCzut2fVP7gRjLVxnenYNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAgUWr%2FbtsD6yrsdaR%2FSCzut2fVP7gRjLVxnenYNk%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;433&quot; height=&quot;156&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;296&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;1478&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRS7Bi/btsEbBAEuUg/n4rnA3DtOlQNz7d499dLY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRS7Bi/btsEbBAEuUg/n4rnA3DtOlQNz7d499dLY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRS7Bi/btsEbBAEuUg/n4rnA3DtOlQNz7d499dLY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRS7Bi%2FbtsEbBAEuUg%2Fn4rnA3DtOlQNz7d499dLY0%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;683&quot; height=&quot;147&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;기본적으로 아무런 설정을 하지 않으면 @Table 의 지정한 name 이 대 소문자를 구분하여 쿼리가 날라간다. 즉 , mysql 에서는 테이블 명이 USERS 였고 JPA entity 의 name 은 users 였기 때문에 테이블을 찾지 못하는것.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;그래서 별도의 namingStrategy를 프로퍼티에서 지정해주거나 엔티티에서 Table name을 지정할 때 대문자로하면 된다..&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그러나 DB tool 내에서의 쿼리콘솔에서도 대소문자 구분하고 싶지 않은데 ... 일단 근본적인 해결방법은 아닌 것 같다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; - &lt;span style=&quot;background-color: #ffffff; color: #16191f; text-align: left;&quot;&gt;lower_case_table_names&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBofAG/btsD5xM3bvu/XXLQnkKqWuTYTs8HZYb3t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBofAG/btsD5xM3bvu/XXLQnkKqWuTYTs8HZYb3t1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBofAG/btsD5xM3bvu/XXLQnkKqWuTYTs8HZYb3t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBofAG%2FbtsD5xM3bvu%2FXXLQnkKqWuTYTs8HZYb3t1%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;587&quot; height=&quot;214&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;mysql 에서 테이블이름에 대해 대 소문자를 구분하는지에 대한 설정을 할 수 있다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;mysql 이 설치된 경로로 들어가 my.cnf 에서 lower_case_table_names = 1 로 설정하면 되지만 AWS RDS에서는 할 수 없기 때문에 RDS 파라미터 설정에서 바꿔줘야한다.&lt;/b&gt;&lt;/blockquote&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;2236&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sYs4H/btsEaNIkCg5/eYAjWHZxj80UWT5592SCAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sYs4H/btsEaNIkCg5/eYAjWHZxj80UWT5592SCAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sYs4H/btsEaNIkCg5/eYAjWHZxj80UWT5592SCAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsYs4H%2FbtsEaNIkCg5%2FeYAjWHZxj80UWT5592SCAk%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;2236&quot; height=&quot;558&quot; data-origin-width=&quot;2236&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2324&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zSVOT/btsEdKX7kxh/zfAaVCxKjgHNZ8Nk2FN4XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zSVOT/btsEdKX7kxh/zfAaVCxKjgHNZ8Nk2FN4XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zSVOT/btsEdKX7kxh/zfAaVCxKjgHNZ8Nk2FN4XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzSVOT%2FbtsEdKX7kxh%2FzfAaVCxKjgHNZ8Nk2FN4XK%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;2324&quot; height=&quot;582&quot; data-origin-width=&quot;2324&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;0 -&amp;gt; 1 로 변경.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2330&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UI9DB/btsEbBtS6L3/jJp7sFqMO0ouJIEH9rVQ90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UI9DB/btsEbBtS6L3/jJp7sFqMO0ouJIEH9rVQ90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UI9DB/btsEbBtS6L3/jJp7sFqMO0ouJIEH9rVQ90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUI9DB%2FbtsEbBtS6L3%2FjJp7sFqMO0ouJIEH9rVQ90%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;2330&quot; height=&quot;754&quot; data-origin-width=&quot;2330&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;뭣 ..&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYmng0/btsEbzpiKoy/KVJkPKEHUFS2i1ydPOAhFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYmng0/btsEbzpiKoy/KVJkPKEHUFS2i1ydPOAhFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYmng0/btsEbzpiKoy/KVJkPKEHUFS2i1ydPOAhFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYmng0%2FbtsEbzpiKoy%2FKVJkPKEHUFS2i1ydPOAhFK%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;722&quot; height=&quot;240&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파라미터 그룹이 연결되어있어서 변경이 안된다니깐 default 로 다시 바꾸고 파라미터 수정 재시도.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhrubO/btsD4IuutRZ/t9RLvA2XB97qe4iaQj7p21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhrubO/btsD4IuutRZ/t9RLvA2XB97qe4iaQj7p21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhrubO/btsD4IuutRZ/t9RLvA2XB97qe4iaQj7p21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhrubO%2FbtsD4IuutRZ%2Ft9RLvA2XB97qe4iaQj7p21%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;2204&quot; height=&quot;498&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;498&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;1604&quot; data-origin-height=&quot;1478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAsVE6/btsEaQSBrPv/cUxTtkYTbNvwyCRNmItXIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAsVE6/btsEaQSBrPv/cUxTtkYTbNvwyCRNmItXIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAsVE6/btsEaQSBrPv/cUxTtkYTbNvwyCRNmItXIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAsVE6%2FbtsEaQSBrPv%2FcUxTtkYTbNvwyCRNmItXIk%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;673&quot; height=&quot;620&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;1478&quot;/&gt;&lt;/span&gt;&lt;/figure&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;b&gt;찾아보니 mysql 8.0 부터는 DB를 최초 생성했을 때만 설정할 수 있다고 나온다..&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.KnownIssuesAndLimitations.html#MySQL.Concepts.ParameterNotes&quot;&gt;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.KnownIssuesAndLimitations.html#MySQL.Concepts.ParameterNotes&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706594510678&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;Known issues and limitations for Amazon RDS for MySQL - Amazon Relational Database Service&quot; data-og-description=&quot;Some existing DB instances have a lower limit. For example, MySQL DB instances created before April 2014 have a file and table size limit of 2 TB. This 2 TB file size limit also applies to DB instances or read replicas created from DB snapshots taken befor&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.KnownIssuesAndLimitations.html#MySQL.Concepts.ParameterNotes&quot; data-og-url=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.KnownIssuesAndLimitations.html#MySQL.Concepts.ParameterNotes&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.KnownIssuesAndLimitations.html#MySQL.Concepts.ParameterNotes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.KnownIssuesAndLimitations.html#MySQL.Concepts.ParameterNotes&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Known issues and limitations for Amazon RDS for MySQL - Amazon Relational Database Service&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Some existing DB instances have a lower limit. For example, MySQL DB instances created before April 2014 have a file and table size limit of 2 TB. This 2 TB file size limit also applies to DB instances or read replicas created from DB snapshots taken befor&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 결론&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;결론적으로 내가 손 댈수 있는건 네이밍 전략밖에 없었다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;application.yml 의 physical-strategy를 CustomNamingStrategy 를 PhysicalNamingStrategy 인터페이스를 구현받아 사용하여 해결하였다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btKaQl/btsEd0zLoix/BK3LcZcQq3fHZNe1MHJxk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btKaQl/btsEd0zLoix/BK3LcZcQq3fHZNe1MHJxk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btKaQl/btsEd0zLoix/BK3LcZcQq3fHZNe1MHJxk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtKaQl%2FbtsEd0zLoix%2FBK3LcZcQq3fHZNe1MHJxk1%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;701&quot; height=&quot;336&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC63QP/btsD7objTxx/NkY0ILC2sdcqrWWikmbS7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC63QP/btsD7objTxx/NkY0ILC2sdcqrWWikmbS7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC63QP/btsD7objTxx/NkY0ILC2sdcqrWWikmbS7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC63QP%2FbtsD7objTxx%2FNkY0ILC2sdcqrWWikmbS7k%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;709&quot; height=&quot;179&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.greenroom.server.api.utils;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class JpaCustomPhysicalNamingStrategy implements PhysicalNamingStrategy {

    @Override
    public Identifier toPhysicalCatalogName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if(identifier == null){
            return null;
        }
        return convertToSnakeCase(identifier);
    }

    @Override
    public Identifier toPhysicalColumnName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if(identifier == null){
            return null;
        }
        return convertToSnakeCase(identifier);
    }

    @Override
    public Identifier toPhysicalSchemaName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if(identifier == null){
            return null;
        }
        return convertToSnakeCase(identifier);
    }

    @Override
    public Identifier toPhysicalSequenceName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if(identifier == null){
            return null;
        }
        return convertToSnakeCase(identifier);
    }

    @Override
    public Identifier toPhysicalTableName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if(identifier == null){
            return null;
        }
        return convertToSnakeCaseAndUpperCase(identifier);
    }

    private Identifier convertToSnakeCase(final Identifier identifier) {
        final String regex = &quot;([a-z])([A-Z])&quot;;
        final String replacement = &quot;$1_$2&quot;;
        final String newName = identifier.getText()
                .replaceAll(regex, replacement)
                .toLowerCase();
        return Identifier.toIdentifier(newName);
    }

    private Identifier convertToSnakeCaseAndUpperCase(final Identifier identifier) {
        final String regex = &quot;([a-z])([A-Z])&quot;;
        final String replacement = &quot;$1_$2&quot;;
        final String newName = identifier.getText()
                .replaceAll(regex, replacement)
                .toUpperCase();
        return Identifier.toIdentifier(newName);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&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;680&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctpCVb/btsEbaQX8Dk/bYMGL5AY8vad3XjaCejJo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctpCVb/btsEbaQX8Dk/bYMGL5AY8vad3XjaCejJo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctpCVb/btsEbaQX8Dk/bYMGL5AY8vad3XjaCejJo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctpCVb%2FbtsEbaQX8Dk%2FbYMGL5AY8vad3XjaCejJo0%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;468&quot; height=&quot;650&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;944&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적용해보면 from 절의 테이블 지정이 대문자로 바뀌어있는 것을 볼 수 있다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>Infra/DB</category>
      <category>JPA naming strategy</category>
      <category>Table doesn't exist</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/191</guid>
      <comments>https://katastrophe.tistory.com/191#entry191comment</comments>
      <pubDate>Tue, 30 Jan 2024 17:04:00 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport ...</title>
      <link>https://katastrophe.tistory.com/190</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmMvBK/btszjEXtMZx/vPQxtkt1w7AX3iL9V105g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmMvBK/btszjEXtMZx/vPQxtkt1w7AX3iL9V105g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmMvBK/btszjEXtMZx/vPQxtkt1w7AX3iL9V105g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmMvBK%2FbtszjEXtMZx%2FvPQxtkt1w7AX3iL9V105g1%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;2356&quot; height=&quot;158&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;158&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;What&amp;nbsp;went&amp;nbsp;wrong:&lt;br /&gt;Execution&amp;nbsp;failed&amp;nbsp;for&amp;nbsp;task&amp;nbsp;':compileJava'.&lt;br /&gt;&amp;gt;&amp;nbsp;java.lang.NoSuchFieldError:&amp;nbsp;Class&amp;nbsp;co&lt;a href=&quot;http://m.sun.tools.javac.tree.JCTree$JCImport&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.sun.tools.javac.tree.JCTree$JCImport&lt;/a&gt;&amp;nbsp;does&amp;nbsp;not&amp;nbsp;have&amp;nbsp;member&amp;nbsp;field&amp;nbsp;'co&lt;a href=&quot;http://m.sun.tools.javac.tree.JCTree&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.sun.tools.javac.tree.JCTree&lt;/a&gt;&amp;nbsp;qualid'&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;950&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMwiKA/btszlmn8dJ3/uUI0OfOAzazn5gIzv4DERk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMwiKA/btszlmn8dJ3/uUI0OfOAzazn5gIzv4DERk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMwiKA/btszlmn8dJ3/uUI0OfOAzazn5gIzv4DERk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMwiKA%2Fbtszlmn8dJ3%2FuUI0OfOAzazn5gIzv4DERk%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;526&quot; height=&quot;101&quot; data-origin-width=&quot;950&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수로 자바 버전이 21 이상인 경우에 gradle build 하니깐 자꾸 실패하길래 관련 글 서칭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/projectlombok/lombok/issues/3393&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/projectlombok/lombok/issues/3393&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698564022785&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;[BUG] lombok 1.8.26 incompatible with JDK21 &amp;middot; Issue #3393 &amp;middot; projectlombok/lombok&quot; data-og-description=&quot;Describe the bug Lombok 1.8.26 does not work with JDK21 (EA build 16) To Reproduce import lombok.Data; @Data public class Person { private String name; } $ javac -version javac 21-ea $ javac -cp lo...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/projectlombok/lombok/issues/3393&quot; data-og-url=&quot;https://github.com/projectlombok/lombok/issues/3393&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Z9dkP/hyUkeDgopR/8eySGYuf3bkZH9XfcDpf91/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/projectlombok/lombok/issues/3393&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/projectlombok/lombok/issues/3393&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Z9dkP/hyUkeDgopR/8eySGYuf3bkZH9XfcDpf91/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[BUG] lombok 1.8.26 incompatible with JDK21 &amp;middot; Issue #3393 &amp;middot; projectlombok/lombok&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Describe the bug Lombok 1.8.26 does not work with JDK21 (EA build 16) To Reproduce import lombok.Data; @Data public class Person { private String name; } $ javac -version javac 21-ea $ javac -cp lo...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N29Eb/btszkRu8My9/379Zhh10zwul7WhfnJyb90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N29Eb/btszkRu8My9/379Zhh10zwul7WhfnJyb90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N29Eb/btszkRu8My9/379Zhh10zwul7WhfnJyb90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN29Eb%2FbtszkRu8My9%2F379Zhh10zwul7WhfnJyb90%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;691&quot; height=&quot;63&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;78&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;lombok 버전을 1.18.30 으로 올리고 다시 빌드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1rzOC/btszlbfSQJM/EcD6bEF0NlKNrTcacd8BP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1rzOC/btszlbfSQJM/EcD6bEF0NlKNrTcacd8BP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1rzOC/btszlbfSQJM/EcD6bEF0NlKNrTcacd8BP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1rzOC%2FbtszlbfSQJM%2FEcD6bEF0NlKNrTcacd8BP0%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;348&quot; height=&quot;97&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>lombok 빌드실패</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/190</guid>
      <comments>https://katastrophe.tistory.com/190#entry190comment</comments>
      <pubDate>Sun, 29 Oct 2023 16:21:56 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] [Level2] 가장큰수 JAVA</title>
      <link>https://katastrophe.tistory.com/189</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42746&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1696918347245&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://school.programmers.co.kr/learn/courses/30/lessons/42746&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DegpE/hyT9B5SuVC/1oPSZTSjimvzClklxFopD0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b9Tzlj/hyT9LOcSiJ/8CYxbFrb2Pu9c21Ly3YoXK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DegpE/hyT9B5SuVC/1oPSZTSjimvzClklxFopD0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b9Tzlj/hyT9LOcSiJ/8CYxbFrb2Pu9c21Ly3YoXK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&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;h1 style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJg6Xp/btsx14BB18J/RobqrvgmkM8TAKA3PKnAoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJg6Xp/btsx14BB18J/RobqrvgmkM8TAKA3PKnAoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJg6Xp/btsx14BB18J/RobqrvgmkM8TAKA3PKnAoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJg6Xp%2Fbtsx14BB18J%2FRobqrvgmkM8TAKA3PKnAoK%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;715&quot; height=&quot;580&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;가장 큰 수를 만들기 위해서 배열안의 수를 큰 수가 될 수 있는 조건으로 정렬을 해야한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;만약 [ 3, 30, 34 ] 안의 우위를 정할 때 기준운 문제에 나와 있듯이 각각 수를 이어 붙였을 때 제일 큰 수로 하면 되므로 정렬 조건은 (s1, s2) -&amp;gt; (s2 + s1).compareTo(s1 + s2)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;추가로 [0, 0, 0, 0] 일 경우엔 0만 리턴하도록 예외처리.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1696918537909&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;
import java.util.List;
class Solution {
    public String solution(int[] numbers) {
        List&amp;lt;String&amp;gt; result = Arrays.stream(numbers)
                .mapToObj(String::valueOf)
                .sorted((s1, s2) -&amp;gt; (s2 + s1).compareTo(s1 + s2))
                .toList();
        return result.get(0).equals(&quot;0&quot;) ? &quot;0&quot; : String.join(&quot;&quot;, result);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1465&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KwNvf/btsxsmYzuuh/qOfsQOnDK3voqUBUjZJUuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KwNvf/btsxsmYzuuh/qOfsQOnDK3voqUBUjZJUuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KwNvf/btsxsmYzuuh/qOfsQOnDK3voqUBUjZJUuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKwNvf%2FbtsxsmYzuuh%2FqOfsQOnDK3voqUBUjZJUuK%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;1465&quot; height=&quot;814&quot; data-origin-width=&quot;1465&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/프로그래머스</category>
      <category>정렬</category>
      <category>프로그래머스 가장 큰 수 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/189</guid>
      <comments>https://katastrophe.tistory.com/189#entry189comment</comments>
      <pubDate>Tue, 10 Oct 2023 15:17:45 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [15486] 퇴사 2 JAVA</title>
      <link>https://katastrophe.tistory.com/188</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15486&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/15486&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1696579569572&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;15486번: 퇴사 2&quot; data-og-description=&quot;첫째 줄에 N (1 &amp;le; N &amp;le; 1,500,000)이 주어진다. 둘째 줄부터 N개의 줄에 Ti와 Pi가 공백으로 구분되어서 주어지며, 1일부터 N일까지 순서대로 주어진다. (1 &amp;le; Ti &amp;le; 50, 1 &amp;le; Pi &amp;le;&amp;nbsp;1,000)&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15486&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15486&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c7JBB1/hyT9GrvQkR/pJW0KY9nnaMdbZgKEDUz2k/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15486&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15486&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c7JBB1/hyT9GrvQkR/pJW0KY9nnaMdbZgKEDUz2k/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;15486번: 퇴사 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 N (1 &amp;le; N &amp;le; 1,500,000)이 주어진다. 둘째 줄부터 N개의 줄에 Ti와 Pi가 공백으로 구분되어서 주어지며, 1일부터 N일까지 순서대로 주어진다. (1 &amp;le; Ti &amp;le; 50, 1 &amp;le; Pi &amp;le;&amp;nbsp;1,000)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbrZGz/btsxp6tGxb7/3DK3kRAZjfD7LJNZBtf84k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbrZGz/btsxp6tGxb7/3DK3kRAZjfD7LJNZBtf84k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbrZGz/btsxp6tGxb7/3DK3kRAZjfD7LJNZBtf84k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbrZGz%2Fbtsxp6tGxb7%2F3DK3kRAZjfD7LJNZBtf84k%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;1684&quot; height=&quot;1123&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1123&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;1626&quot; data-origin-height=&quot;801&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT3h2u/btsxp5BFVFY/x0Z03oMjGDhc97tkp3PgKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT3h2u/btsxp5BFVFY/x0Z03oMjGDhc97tkp3PgKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT3h2u/btsxp5BFVFY/x0Z03oMjGDhc97tkp3PgKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT3h2u%2Fbtsxp5BFVFY%2Fx0Z03oMjGDhc97tkp3PgKK%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;1626&quot; height=&quot;801&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;801&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;하루씩 순차적으로 진행하면서 특정 일의 상담을 진행하고 나서 받는 총 합 이익을 갱신 시켜주면 된다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;특정일을 i&amp;nbsp; &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;상담 걸리는 시간을 A(i) &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;페이를 P(i)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;페이 총합을 S(i) 라고 했을 때 점화식은&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;S(i + A(i)) = max( S(i + A(i)), S(i)+P(i) ) 가 된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;추가로)&lt;br /&gt;i 의 범위는 i 번째 상담을 끝 났을 때 i+1 일에 정산 받는다는 걸 가정하여 n+1 까지.&lt;br /&gt;해당 점화식은 상향식으로 하고 있기 때문에 기록한 최대 페이를 지나온 곳 까지 기록하기 위해 maxPay 를 계속 갱신.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1696580226475&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {


    static int n;
    static int[] dp;
    static Consult[] arr;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        n = Integer.parseInt(br.readLine());
        dp = new int[n+2];
        arr = new Consult[n+2];
        for(int i=1;i&amp;lt;=n;i++){
            arr[i] = new Consult(br.readLine());
        }
        arr[n+1] = new Consult(&quot;0 0&quot;);

        int maxPay = Integer.MIN_VALUE;
        for(int i=1;i&amp;lt;=n+1;i++){

            if(maxPay &amp;lt; dp[i]){
                maxPay = dp[i];
            }

            if(arr[i].days+i &amp;gt; n+1)
                continue;

            dp[arr[i].days+i] = Math.max(dp[arr[i].days+i],maxPay+arr[i].pays);
        }

        System.out.println(maxPay);
    }

    static class Consult{
        int days;
        int pays;

        public Consult(String in) {
            int[] input = Arrays.stream(in.split(&quot; &quot;))
                     .mapToInt(Integer::parseInt).toArray();
            this.days = input[0];
            this.pays = input[1];
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1563&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wzxp7/btsxp4Qjr3b/IlOtAeq57NTJh8clFPBXR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wzxp7/btsxp4Qjr3b/IlOtAeq57NTJh8clFPBXR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wzxp7/btsxp4Qjr3b/IlOtAeq57NTJh8clFPBXR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWzxp7%2Fbtsxp4Qjr3b%2FIlOtAeq57NTJh8clFPBXR1%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;1563&quot; height=&quot;209&quot; data-origin-width=&quot;1563&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>다이나믹프로그래밍</category>
      <category>백준 14586 java</category>
      <category>백준 퇴사2 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/188</guid>
      <comments>https://katastrophe.tistory.com/188#entry188comment</comments>
      <pubDate>Fri, 6 Oct 2023 17:21:08 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] [Level2] 과제진행하기 JAVA</title>
      <link>https://katastrophe.tistory.com/187</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/176962#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/176962#&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694936682316&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://school.programmers.co.kr/learn/courses/30/lessons/176962#&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cELTqK/hyTV0SL2oc/29q3X0Jnuz47MFeRkGXUA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bsqI5k/hyTV0Fc7oY/VhlkOkOM7itvfKzkKpCpak/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/176962#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/176962#&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cELTqK/hyTV0SL2oc/29q3X0Jnuz47MFeRkGXUA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bsqI5k/hyTV0Fc7oY/VhlkOkOM7itvfKzkKpCpak/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&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;h1 style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm3JT1/btst88vJ0hG/jawYRuZHiKKK3kfmUX6kkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm3JT1/btst88vJ0hG/jawYRuZHiKKK3kfmUX6kkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm3JT1/btst88vJ0hG/jawYRuZHiKKK3kfmUX6kkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm3JT1%2Fbtst88vJ0hG%2FjawYRuZHiKKK3kfmUX6kkK%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;615&quot; height=&quot;403&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;640&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;958&quot; data-origin-height=&quot;809&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/birfOj/btsut4rkqjs/jO3NAFC1zc10O2yKZtQH6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/birfOj/btsut4rkqjs/jO3NAFC1zc10O2yKZtQH6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/birfOj/btsut4rkqjs/jO3NAFC1zc10O2yKZtQH6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbirfOj%2Fbtsut4rkqjs%2FjO3NAFC1zc10O2yKZtQH6k%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;614&quot; height=&quot;519&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;809&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;952&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv4ITA/btsuel18JTG/WoEcNikIMkg15lARLRLRak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv4ITA/btsuel18JTG/WoEcNikIMkg15lARLRLRak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv4ITA/btsuel18JTG/WoEcNikIMkg15lARLRLRak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv4ITA%2Fbtsuel18JTG%2FWoEcNikIMkg15lARLRLRak%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;641&quot; height=&quot;255&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;과제 진행의 우선순위는 1. 시간&amp;nbsp; 2. 최근에 멈춘 과제 이다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 과제를 시간순으로 먼저 정렬해두고 하나씩 진행&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 현재 과제를 딜레이 시키지 않고 처리할 수 있는 경우 처리&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp; 2-1. 밀린 과제가 있는 경우 밀린 과제 스택에서 하나를 꺼내서 다음 처리 과제 대상에 넣음&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp; 2-2. 밀린 과제가 없는 경우 다음 과제를 진행&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 현재 과제를 딜레이 시켜야 할 경우는 현재 시간과 다음과제 시작시간을 비교해서 시간차이만큼 처리하고 스택에 다시 삽입.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. 시간순 과제 리스트가 빌 경우 밀린 과제를 하나씩 꺼내서 처리&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694937253189&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.time.LocalDateTime;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

public class Solution{

    private LinkedList&amp;lt;Plan&amp;gt; list = new LinkedList&amp;lt;&amp;gt;();
    private Stack&amp;lt;Plan&amp;gt; delayPlans = new Stack&amp;lt;&amp;gt;();
    private ArrayList&amp;lt;String&amp;gt; answer = new ArrayList&amp;lt;&amp;gt;();
    public String[] solution(String[][] plans) {

        list = Arrays.stream(plans)
                .map(Plan::new)
                .sorted((o1, o2) -&amp;gt; o1.time.compareTo(o2.time))
                .collect(Collectors.toCollection(LinkedList::new));

        LocalDateTime currentTime = null;
        boolean delayedPlanProcFlag = false;

        while (!list.isEmpty() || !delayPlans.isEmpty()) {

            Plan plan = null;
            
            if(delayedPlanProcFlag){
                plan = delayPlans.pop();
                delayedPlanProcFlag = false;
            }else{
                plan = list.poll();
                currentTime = plan.time;
            }

            if(!list.isEmpty()){
                Plan nextPlan = list.peek();
                LocalDateTime estimatedTime = currentTime.plusMinutes(plan.duration);

                int criteria = estimatedTime.compareTo(nextPlan.time);

                if(criteria &amp;gt; 0){ // 현재 plan 의 duration 을 처리 못할 경우
                    delayPlans.add(plan.processPlanPartial(currentTime,nextPlan.time));
                }else{
                    answer.add(plan.name);

                    if(criteria &amp;lt; 0 &amp;amp;&amp;amp; !delayPlans.isEmpty()){
                        delayedPlanProcFlag = true;
                        currentTime = estimatedTime;
                    }
                }
            }else{
                answer.add(plan.name);
                while (!delayPlans.isEmpty()){
                    answer.add(delayPlans.pop().name);
                }
            }

        }
        return answer.toArray(String[]::new);
    }


    static class Plan{
        String name;
        LocalDateTime time;
        int duration;

        public Plan(String[] plan){
            this.name = plan[0];
            String[] splitTime = plan[1].split(&quot;:&quot;);
            this.time = LocalDateTime.of(0, Month.JANUARY,1,Integer.parseInt(splitTime[0]), Integer.parseInt(splitTime[1]));
            this.duration = Integer.parseInt(plan[2]);
        }
        public Plan processPlanPartial(LocalDateTime currentTime, LocalDateTime nextPlanTime){
            long diff = Math.abs(ChronoUnit.MINUTES.between(nextPlanTime, currentTime));
            this.duration -= diff;
            return this;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘,PS/프로그래머스</category>
      <category>과제진행하기</category>
      <category>프로그래머스</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/187</guid>
      <comments>https://katastrophe.tistory.com/187#entry187comment</comments>
      <pubDate>Sun, 17 Sep 2023 16:54:36 +0900</pubDate>
    </item>
    <item>
      <title>사람인 기업 필터링 기능 만들기 (with Tampermonkey)</title>
      <link>https://katastrophe.tistory.com/186</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Tampermonkey&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Tampermonkey&lt;/span&gt;는 &lt;u&gt;웹 브라우저 확장 프로그램(chrome extention)&lt;/u&gt;&amp;nbsp;중 하나로, 주로 &lt;u&gt;사용자가 웹 페이지의 동작을 수정하거나 개선하기 위해 스크립트를 실행할 수 있게 해주는 도구&lt;/u&gt;이다. Tampermonkey는 대부분의 주요 웹 브라우저에서 사용할 수 있으며, 사용자 지정 스크립트를 실행하여 웹 페이지에 자신만의 기능을 추가하거나 웹 사이트의 동작을 수정할 수 있다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tampermonkey를 사용하면 할 수 있는 것들:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. &lt;span style=&quot;color: #f89009;&quot;&gt;사용자 지정 스크립트 실행&lt;/span&gt;: Tampermonkey를 통해 &lt;u&gt;웹 페이지에 JavaScript 스크립트를 삽입하고 실행가능&lt;/u&gt;. 이를 통해 웹 페이지의 내용을 수정하거나 기능을 확장할 수 있다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. &lt;span style=&quot;color: #f89009;&quot;&gt;광고 차단&lt;/span&gt;: Tampermonkey를 사용하여 광고 차단 스크립트를 실행하면 광고를 숨기거나 제거가능.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. &lt;span style=&quot;color: #f89009;&quot;&gt;웹 페이지 개선&lt;/span&gt;: Tampermonkey를 사용하여 웹 페이지의 사용자 경험을 개선하거나 특정 기능을 추가할 수 있음.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;ex. 웹 페이지의 테마를 변경&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. &lt;span style=&quot;color: #f89009;&quot;&gt;사용자 지정 스크립트 저장 및 관리&lt;/span&gt;: Tampermonkey는 사용자가 작성한 스크립트를 저장하고 관리하는 기능을 제공.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tampermonkey를 사용하려면 먼저 해당 브라우저 확장 프로그램 스토어에서 Tampermonkey 확장을 설치해야함.&lt;/b&gt;&lt;br /&gt;&lt;b&gt; 그런 다음 원하는 스크립트를 작성하거나 Tampermonkey 스크립트 저장소에서 다른 사용자가 작성한 스크립트를 가져와 사용이 가능.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;사람인 기업 필터링 기능 추가해보기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;구직 플랫폼 중 하나인 사람인에서 애드블록 처럼 안 보고 싶은 기업의 공고 (악성 기업이나 인력장사 보도방 공고) 는 계속 안보이게 하고싶어서 이 Tampermonkey를 이용하여 필터링 하는 기능을 스크립트를 작성하여 만들어 볼 것이다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우선 &lt;span style=&quot;color: #f89009;&quot;&gt;Tampermonkey 를 설치&lt;/span&gt;하고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko&quot;&gt;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1693582818928&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;Tampermonkey&quot; data-og-description=&quot;Change the web at will with userscripts&quot; data-og-host=&quot;chrome.google.com&quot; data-og-source-url=&quot;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko&quot; data-og-url=&quot;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCwSwh/hyTL7rlbnH/7KdU4AezUPxMjN2zKwzP3K/img.jpg?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128&quot;&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCwSwh/hyTL7rlbnH/7KdU4AezUPxMjN2zKwzP3K/img.jpg?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128');&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;Tampermonkey&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Change the web at will with userscripts&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chrome.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;밑의 &lt;u&gt;openuserJS&lt;/u&gt; 링크에 들어가서 &lt;span style=&quot;color: #f89009;&quot;&gt;유저스크립트를 설치&lt;/span&gt;만 하면 된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( 계속 업데이트 중)&lt;br /&gt;&lt;a href=&quot;https://openuserjs.org/scripts/katastrophe/jobBlocking_saramin_ver_0.1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://openuserjs.org/scripts/katastrophe/jobBlocking_saramin_ver_0.1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693584155150&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;About | jobBlocking_saramin_ver_0.1 | Userscripts | OpenUserJS&quot; data-og-description=&quot;Are you sure you want to flag this script for potential inspection by a Moderator?&quot; data-og-host=&quot;openuserjs.org&quot; data-og-source-url=&quot;https://openuserjs.org/scripts/katastrophe/jobBlocking_saramin_ver_0.1&quot; data-og-url=&quot;https://openuserjs.org/scripts/katastrophe/jobBlocking_saramin_ver_0.1&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://openuserjs.org/scripts/katastrophe/jobBlocking_saramin_ver_0.1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://openuserjs.org/scripts/katastrophe/jobBlocking_saramin_ver_0.1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;About | jobBlocking_saramin_ver_0.1 | Userscripts | OpenUserJS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Are you sure you want to flag this script for potential inspection by a Moderator?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;openuserjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEZpT3/btssMHs5UVb/FH2jihwlatkE02l78Zx60K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEZpT3/btssMHs5UVb/FH2jihwlatkE02l78Zx60K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEZpT3/btssMHs5UVb/FH2jihwlatkE02l78Zx60K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEZpT3%2FbtssMHs5UVb%2FFH2jihwlatkE02l78Zx60K%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;1249&quot; height=&quot;383&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우측 상단의 install 클릭후 설치를 누르면 끝.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WgV8K/btssPxpM9Xa/bE7W087kTPL1IZnjlNsfY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WgV8K/btssPxpM9Xa/bE7W087kTPL1IZnjlNsfY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WgV8K/btssPxpM9Xa/bE7W087kTPL1IZnjlNsfY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWgV8K%2FbtssPxpM9Xa%2FbE7W087kTPL1IZnjlNsfY0%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;1902&quot; height=&quot;373&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;구현 과정&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;( 글 접어 놨습니다. )&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;319&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCd0tj/btssVJ93OxB/cweiWt0iKqz5Os9beZiLLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCd0tj/btssVJ93OxB/cweiWt0iKqz5Os9beZiLLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCd0tj/btssVJ93OxB/cweiWt0iKqz5Os9beZiLLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCd0tj%2FbtssVJ93OxB%2FcweiWt0iKqz5Os9beZiLLk%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;319&quot; height=&quot;255&quot; data-origin-width=&quot;319&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;새 스크립트 만들기 클릭&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JKmaP/btssMIesFjh/oHkZOdE8armXKKFalm7wd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JKmaP/btssMIesFjh/oHkZOdE8armXKKFalm7wd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JKmaP/btssMIesFjh/oHkZOdE8armXKKFalm7wd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJKmaP%2FbtssMIesFjh%2FoHkZOdE8armXKKFalm7wd0%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;1902&quot; height=&quot;590&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693582989894&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://*/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...
})();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저기 function 안에 작동 시킬 원하는 스크립트를 적으면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중요한건 위의 Userscript 의 메타블록 작성인데 필수 메타태그는 공식문서를 참조하면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.tampermonkey.net/documentation.php?locale=en&quot;&gt;https://www.tampermonkey.net/documentation.php?locale=en&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1693583080953&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;Documentation | Tampermonkey&quot; data-og-description=&quot;Documentation&quot; data-og-host=&quot;www.tampermonkey.net&quot; data-og-source-url=&quot;https://www.tampermonkey.net/documentation.php?locale=en&quot; data-og-url=&quot;https://www.tampermonkey.net/documentation.php?locale=en&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KDCeD/hyTMfJGu0p/fiifqwypqguLUKJXu2Kqt1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.tampermonkey.net/documentation.php?locale=en&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.tampermonkey.net/documentation.php?locale=en&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KDCeD/hyTMfJGu0p/fiifqwypqguLUKJXu2Kqt1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Documentation | Tampermonkey&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Documentation&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.tampermonkey.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사람인 기업 필터링 스크립트 완성본&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1693583105728&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ==UserScript==
// @name         jobBlocking_saramin_ver_0.1
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       katastrophe - nicebyy@naver.com
// @match        https://www.saramin.co.kr/zf_user/jobs/list/*
// @icon         https://www.google.com/s2/favicons?sz=64&amp;amp;domain=tampermonkey.net
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @license MIT
// @copyright 2023, katastrophe (https://openuserjs.org/users/katastrophe)
// ==/UserScript==

(function () {

    let buttonMap = new Map();

    const createData = (key, element) =&amp;gt; {

        const companyName = element.querySelector('.company_nm &amp;gt; a').innerText.replaceAll(' ', '');
        const jobDescription = element.querySelector('.notification_info &amp;gt; .job_tit &amp;gt; a &amp;gt; span').innerText.replaceAll(' ', '');

        const jobInfo = {
            key: key,
            companyName: companyName,
            jobDescription: jobDescription
        };
        return jobInfo;
    };

    const createButton = (key, element) =&amp;gt; {

        let button = document.createElement('button');
        // html
        button.type = 'button';
        button.innerHTML = key;
        button.className = 'btn_del';

        //css
        button.style.marginTop = '2px';
        button.style.marginRight = '8px';

        button.style.display = 'inline-block';
        button.style.marginLeft = '4px';
        button.style.width = '13px';
        button.style.height = '17px';
        button.style.verticalAlign = 'top';
        button.style.fontSize = '0';
        button.style.background = 'url(//www.saraminimage.co.kr/sri/person/search_panel/spr_search_panel.png) no-repeat 2px -186px';

        const divBox = element.querySelector('.company_nm');
        divBox.style.width = '200px';
        const pos = element.querySelector('.company_nm &amp;gt; .interested_corp');

        divBox.insertBefore(button, pos);
        button.addEventListener(&quot;click&quot;, () =&amp;gt; {

            let result = GM_getValue(key, null);

            if (result == null) {
                element.style.display = 'none';
                GM_setValue(key, createData(key, element));
                buttonMap.set(key, false);
                displayAllJobs(false);
            }
        });
    };

    const displayAllJobs = (isInit) =&amp;gt; { // Find all job elements

        if(isInit){
            buttonMap = new Map();
        }
        const elements = document.querySelectorAll('[id^=&quot;rec-&quot;]');

        elements.forEach(element =&amp;gt; {
            const recId = element.getAttribute('id'); // jobKey
            const recValue = GM_getValue(recId, null);

            if (recValue !== null) { 
                element.style.display = 'none';

            } else if (isInit) {
                buttonMap.set(recId, true);
                createButton(recId, element);
            }

        });
    }

    const removeBlockingList = ()=&amp;gt;{
        for(let key of GM_listValues()){
            GM_deleteValue(key);
        }
    };

    const createRemoveButton = ()=&amp;gt;{

        let button = document.createElement('button');
        // html
        button.type = 'button';
        button.className = 'sri_btn_md';

        //css
        button.style.display = 'inline-flex';
        button.style.marginLeft = '-34%';

        let buttonText = document.createElement('span');
        buttonText.innerHTML = 'blocking 초기화';
        buttonText.className = 'sri_btn_immediately';

        button.appendChild(buttonText);

        const divBox = document.querySelector('.list_info');
        const pos = document.querySelector('.list_select');

        divBox.insertBefore(button, pos);
        button.addEventListener(&quot;click&quot;, () =&amp;gt; {

            if (!confirm(&quot;Blocking List 를 초기화 합니다..&quot;)) {
                alert(&quot;취소를 누르셨습니다.&quot;);
            } else {
                alert(&quot;초기화 완료.&quot;);
                removeBlockingList();
                location.reload();
            }
        });
    }

    document.querySelector('#page_count').addEventListener(&quot;change&quot;, () =&amp;gt; {
        setTimeout(function(){
            displayAllJobs(true);
        },1000);
    });
    document.querySelector('#sort').addEventListener(&quot;change&quot;, () =&amp;gt; {
        setTimeout(function(){
            displayAllJobs(true);
        },1000);
    });
    if(document.querySelector('#show_applied_recruit')!=null){
        document.querySelector('#show_applied_recruit').addEventListener(&quot;change&quot;, () =&amp;gt; {
            setTimeout(function(){
                displayAllJobs(true);
            },1000);
        });
    }
    document.querySelector('#filter_ai_head_hunting').addEventListener(&quot;change&quot;, () =&amp;gt; {
        setTimeout(function(){
            displayAllJobs(true);
        },1000);
    });
    document.querySelector('#filter_quick_apply').addEventListener(&quot;change&quot;, () =&amp;gt; {
        setTimeout(function(){
            displayAllJobs(true);
        },1000);
    });
        'use strict';
        createRemoveButton();
        displayAllJobs(true);
    
})();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1040&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2a3aK/btssNt2t55G/tkPyjWbxJkCHE6OOmLNjTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2a3aK/btssNt2t55G/tkPyjWbxJkCHE6OOmLNjTk/img.png&quot; data-alt=&quot;지우기 전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2a3aK/btssNt2t55G/tkPyjWbxJkCHE6OOmLNjTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2a3aK%2FbtssNt2t55G%2FtkPyjWbxJkCHE6OOmLNjTk%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;1040&quot; height=&quot;650&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;650&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;b&gt;지역별로 검색을 했을 때 못 보던 &lt;span style=&quot;color: #f89009;&quot;&gt;X 표시와 blocking 초기화 버튼&lt;/span&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;&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;1047&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btZOul/btssMIFzwyt/K3omj4htZNamFMTyFNbXq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btZOul/btssMIFzwyt/K3omj4htZNamFMTyFNbXq1/img.png&quot; data-alt=&quot;지운 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btZOul/btssMIFzwyt/K3omj4htZNamFMTyFNbXq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtZOul%2FbtssMIFzwyt%2FK3omj4htZNamFMTyFNbXq1%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;1047&quot; height=&quot;660&quot; data-origin-width=&quot;1047&quot; data-origin-height=&quot;660&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;b&gt;다음과 같이 없어진다. 만약 다시 보이게 하고 싶으면 blocking 초기화 버튼을 누르면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xjVFA/btssMCehnGy/H2Pev7gv0NWc62IrPOPStK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xjVFA/btssMCehnGy/H2Pev7gv0NWc62IrPOPStK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xjVFA/btssMCehnGy/H2Pev7gv0NWc62IrPOPStK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxjVFA%2FbtssMCehnGy%2FH2Pev7gv0NWc62IrPOPStK%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;451&quot; height=&quot;127&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;127&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;449&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmKBtq/btssNtIbZTM/nHHrITovbZAlsz0ehEgU3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmKBtq/btssNtIbZTM/nHHrITovbZAlsz0ehEgU3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmKBtq/btssNtIbZTM/nHHrITovbZAlsz0ehEgU3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmKBtq%2FbtssNtIbZTM%2FnHHrITovbZAlsz0ehEgU3K%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;449&quot; height=&quot;133&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;133&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;데이터 관련 스토리지는 GM 관련 내부 Strorage API 를 사용하였다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터를 확인하고 싶다면 설정에서 모드를 상급자로 변경 뒤&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bspv38/btssTggJzC7/dcu1FUT7ZS608m1dcewjgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bspv38/btssTggJzC7/dcu1FUT7ZS608m1dcewjgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bspv38/btssTggJzC7/dcu1FUT7ZS608m1dcewjgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbspv38%2FbtssTggJzC7%2Fdcu1FUT7ZS608m1dcewjgk%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;1917&quot; height=&quot;642&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다시 에디터쪽으로 가보면 Storage 탭이 새로 생긴 것을 볼 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저장포맷은 Key-value 방식의 Json 을 사용하는 듯 하다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BlqEZ/btssPxiXnuF/KRafau0MLGb6LfvOFC4UdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BlqEZ/btssPxiXnuF/KRafau0MLGb6LfvOFC4UdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BlqEZ/btssPxiXnuF/KRafau0MLGb6LfvOFC4UdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBlqEZ%2FbtssPxiXnuF%2FKRafau0MLGb6LfvOFC4UdK%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;1005&quot; height=&quot;421&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;421&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crWlj4/btssNkj8mLi/kyIo7gbpK2QbAQzUpIGci1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crWlj4/btssNkj8mLi/kyIo7gbpK2QbAQzUpIGci1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crWlj4/btssNkj8mLi/kyIo7gbpK2QbAQzUpIGci1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrWlj4%2FbtssNkj8mLi%2FkyIo7gbpK2QbAQzUpIGci1%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;1160&quot; height=&quot;75&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;페이징 방식을 변경할 때 버튼이 생성되고나서 새로운 페이징결과가 적용되기 때문에 이는 직접제어 할 수 없어서 setTimeout 을 이용하여 1초뒤에 버튼이 보이게 만들었다. 네트워크 환경이 불안정하거나 페이지를 불러오는데 1초이상 걸린다면 적용이 안될 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bADUfh/btssUPWZNqB/kZqU4CuNavmMVZqt9xYFyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bADUfh/btssUPWZNqB/kZqU4CuNavmMVZqt9xYFyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bADUfh/btssUPWZNqB/kZqU4CuNavmMVZqt9xYFyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbADUfh%2FbtssUPWZNqB%2FkZqU4CuNavmMVZqt9xYFyK%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;689&quot; height=&quot;531&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그럴땐 소스 내부의 setTimeout 시간을 1000( 1000ms = 1초 )&amp;nbsp; 보다 높은 값으로 설정하면 동작한다.&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;</description>
      <category>etc/개인프로젝트</category>
      <category>Tampermonkey</category>
      <category>사람인 기업필터링</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/186</guid>
      <comments>https://katastrophe.tistory.com/186#entry186comment</comments>
      <pubDate>Sat, 2 Sep 2023 01:07:43 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Websocket 과 ReverseProxy</title>
      <link>https://katastrophe.tistory.com/185</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Websocket&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;말 그대로 Web 을 이용한 Socket 통신이다. 즉 , HTTP 통신을 이용한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;웹 브라우저 &amp;lt;-&amp;gt; WAS 간의 실시간 양방향 통신을 위해 사용한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;처음 핸드쉐이크를 요청할 때 까진 HTTP 와 통신과정이 같고 그 이후엔 TCP 커넥션을 이용하여 실시간으로 통신한다는 것이 특징이다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V6wtk/btsqYoBwSLu/bVDd6EIHSkJGjFpvEoPnCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V6wtk/btsqYoBwSLu/bVDd6EIHSkJGjFpvEoPnCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V6wtk/btsqYoBwSLu/bVDd6EIHSkJGjFpvEoPnCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV6wtk%2FbtsqYoBwSLu%2FbVDd6EIHSkJGjFpvEoPnCk%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;524&quot; height=&quot;404&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;여기서 특별한 헤더가 추가 된다는 것이 특징인데 , Upgrade 헤더이다.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;HTTP 요청 헤더에서 Websocket 통신을 위해 Upgrade 헤더를 추가해서 요청을 보내는데 이 요청이 WAS 쪽에서 받아 들여져야 그 이후 ws:// or wss:// 프로토콜을 통해 양방향 통신이 가능한 상태가 된다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW8CF0/btsq1aPezlh/ESvbEbnncWy1Z8GtxXw2A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW8CF0/btsq1aPezlh/ESvbEbnncWy1Z8GtxXw2A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW8CF0/btsq1aPezlh/ESvbEbnncWy1Z8GtxXw2A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW8CF0%2Fbtsq1aPezlh%2FESvbEbnncWy1Z8GtxXw2A1%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;524&quot; height=&quot;197&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;250&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reverse Proxy&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;웹 소켓 서버 정보를 클라이언트쪽에서 (js 단에서) 가지고 있는 것은 상당히 위험하다. 즉 , 웹 클라이언트에서는 실제로 내가 접속하고 있는 서버의 정보를 몰라야 한다는 것이 보안측면에서도 안전하다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;그래서 사용된 방식이 ReverseProxy 방식인데 엔드 유저들은 실제 웹 소켓서버로 요청을 보낼 때 예를 들어 Backend Server 1 의 ip 와 port 로 직접적으로 요청이 하는 방식이 아닌 우선 웹 서버 80 포트로 요청을 보내면 아파치 서버의 conf 설정을 통해 실제 Was 로 포워딩 되는 방식으로 요청이 가게 하는 방식이다. 이를 리버스 프록싱 방식이라고 한다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btm3Is/btsqYTOX2iT/wQbtXam8oPgs8Tj1tK7s81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btm3Is/btsqYTOX2iT/wQbtXam8oPgs8Tj1tK7s81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btm3Is/btsqYTOX2iT/wQbtXam8oPgs8Tj1tK7s81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtm3Is%2FbtsqYTOX2iT%2FwQbtXam8oPgs8Tj1tK7s81%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;722&quot; height=&quot;414&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;547&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Apache 설정&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1691747042525&quot; class=&quot;apache&quot; data-ke-language=&quot;apache&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Virtualhost *:80&amp;gt;
	ProxyPreserveHost On
	ProxyRequests Off

	RewriteEngine On
    RewriteCond ${HTTP:Upgrade} websocket [NC]
  	RewriteCond ${HTTP:Connection} upgrade [NC]
    
	RewriteRule /(.*\/websocket) ws://localhost:8090/$1 [P,L]
	RewriteRule /(.*) http://localhost:8090/$1 [P,L]
    
	ProxyPass / http://localhost:8090/
	ProxyPassReverse / http://localhost:8090/
&amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;실제 우리 서버 아파치 설정을 보여주긴 보안상 좀 그러니 예시중 일부를 가져왔다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;HTTP 요청으로 엔드포인트가 /websocket 으로 끝난다면 ws://localhost:8090 으로 요청을 돌려버린다는 설정이다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;즉 , 메인도메인으로 요청할 때 엔드포인트에 따라서 프록시을 적용할 지 말지를 결정할 수 있다는 것이다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그러나 이것을 적용할 때 문제점이 있었다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;/websock 처럼 엔드포인트로 프록시 조건이 들어갈 때 HTTP 요청도 받을 수 있게 해야한다는 것.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;다시말해, &lt;u&gt;웹 소켓 통신만 한다고 해서 ws:// 로 무조건 보내면 처음 HTTP 핸드쉐이크를 실패&lt;/u&gt;한다는 것이다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691748849223&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker(&quot;/topic&quot;);
        config.setApplicationDestinationPrefixes(&quot;/app&quot;);
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(&quot;/websock&quot;).setAllowedOrigins(&quot;*&quot;).setHandshakeHandler(new WSHandShakeHandler()).withSockJS();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;웹 소켓 서버의 Configuration 쪽에서 endPoint 를 /websock 으로 받게 했기 때문에 apache conf 파일의 내용을 다음과 같이 순서를 변경한다.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;이렇게 하지 않으면 만약 SockJs를 쓰는 경우엔 웹 클라이언트단에서 콘솔 오류로 info?t=~~~ 404 not Found 를 뱉을 것이다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1691749544395&quot; class=&quot;apache&quot; data-ke-language=&quot;apache&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Virtualhost *:80&amp;gt;
	ProxyPreserveHost On
	ProxyRequests Off

	RewriteEngine On
    RewriteCond ${HTTP:Upgrade} websocket [NC]
  	RewriteCond ${HTTP:Connection} upgrade [NC]
    
    RewriteRule /(.*) http://localhost:8090/$1 [P,L]
	RewriteRule /(.*\/websocket) ws://localhost:8090/$1 [P,L]
    
	ProxyPass / http://localhost:8090/
	ProxyPassReverse / http://localhost:8090/
&amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>apache reverseProxy</category>
      <category>ReverseProxy</category>
      <category>sockjs</category>
      <category>spring websocket</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/185</guid>
      <comments>https://katastrophe.tistory.com/185#entry185comment</comments>
      <pubDate>Fri, 11 Aug 2023 19:32:01 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Redisson Remote Service 분해해보기</title>
      <link>https://katastrophe.tistory.com/179</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Redisson&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Reddison&lt;/b&gt; 은 &lt;b&gt;Redis 의 클라이언트&lt;/b&gt;중 하나로 Jedis, Lecttuce 와 같은 &amp;lsquo;분산&amp;rsquo; 서비스 또는 락을 중점으로 이용할 때 사용된다. 이번 포스팅은 락 관련은 넘어가고 Remote Service 에 대해 포스팅하고자 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Redisson Remote Service&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;기존의 다른 Redis 클라이언트로는 할 수 없었던 Redisson 의 분산 서비스의 특징이다. &lt;br /&gt;&lt;b&gt;RemoteService&lt;/b&gt; 는 크게 서버 인스턴스와 클라이언트 인스턴스로 두 종류로 구성되어있다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P9Mhp/btslcDqshbm/zNORKQwG02neBqiJfVDWE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P9Mhp/btslcDqshbm/zNORKQwG02neBqiJfVDWE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P9Mhp/btslcDqshbm/zNORKQwG02neBqiJfVDWE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP9Mhp%2FbtslcDqshbm%2FzNORKQwG02neBqiJfVDWE1%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;573&quot; height=&quot;256&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Lx8Y/btslgizkpcy/nefSLCT1AeiCAHCJBPJM0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Lx8Y/btslgizkpcy/nefSLCT1AeiCAHCJBPJM0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Lx8Y/btslgizkpcy/nefSLCT1AeiCAHCJBPJM0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Lx8Y%2Fbtslgizkpcy%2FnefSLCT1AeiCAHCJBPJM0K%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;519&quot; height=&quot;518&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;518&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;824&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/slu67/btsldQb1plc/bTEEACcpCDBeW1egcLqA1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/slu67/btsldQb1plc/bTEEACcpCDBeW1egcLqA1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/slu67/btsldQb1plc/bTEEACcpCDBeW1egcLqA1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fslu67%2FbtsldQb1plc%2FbTEEACcpCDBeW1egcLqA1K%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;547&quot; height=&quot;414&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;623&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 생성&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 환경 : &lt;span data-token-index=&quot;1&quot;&gt;springboot 3.1.0&lt;/span&gt;, Gradle, intellij, window10, Redis, Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redis 설치하기&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker-compose&lt;/b&gt; 를 통해 간단하게 테스트용 로컬 레디스를 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(관련 설명링크 : &lt;a href=&quot;https://learn.microsoft.com/ko-kr/azure/cognitive-services/containers/docker-compose-recipe&quot;&gt;https://learn.microsoft.com/ko-kr/azure/cognitive-services/containers/docker-compose-recipe&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;window의 wsl2 를 이용하여 docker 를 구동하였음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(mac 을 사용한다면 brew 와 같은 패키지 관리자를 설치후 docker를 설치하면 됨)&lt;/p&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;docker-compose.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1687712721444&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version : '3.8'
services :
  redis-remoteService :
    container_name : redis-remoteService
    image : redis:latest
    restart: always
    ports: 
      - 6379:6379&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AbUj1/btslaGVK2TT/zBCSnbKhibPfXlyusNIuQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AbUj1/btslaGVK2TT/zBCSnbKhibPfXlyusNIuQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AbUj1/btslaGVK2TT/zBCSnbKhibPfXlyusNIuQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAbUj1%2FbtslaGVK2TT%2FzBCSnbKhibPfXlyusNIuQK%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;1889&quot; height=&quot;266&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;266&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;1025&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxilqW/btslas4xcc4/WH5mfNQcJZPCeGcWlULb3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxilqW/btslas4xcc4/WH5mfNQcJZPCeGcWlULb3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxilqW/btslas4xcc4/WH5mfNQcJZPCeGcWlULb3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxilqW%2Fbtslas4xcc4%2FWH5mfNQcJZPCeGcWlULb3k%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;1025&quot; height=&quot;185&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 최상단위치에 docker-compose 파일을 넣어두고 백그라운드로 실행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6379 포트로 잘 돌아가는 것을 확인. (Docker Desktop)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2086&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLGFjd/btsla0NeMQf/sPqSZj6R0Zutv2XLKxo8x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLGFjd/btsla0NeMQf/sPqSZj6R0Zutv2XLKxo8x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLGFjd/btsla0NeMQf/sPqSZj6R0Zutv2XLKxo8x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLGFjd%2Fbtsla0NeMQf%2FsPqSZj6R0Zutv2XLKxo8x1%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;2086&quot; height=&quot;164&quot; data-origin-width=&quot;2086&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- P3x Redis UI 툴로 확인&lt;/b&gt;해보기 (혹은 Redis insight 를 추천)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥션 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;1031&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6pwHY/btslbxxgcrU/ZHNIEJQ9gKkEnMRmOnkXo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6pwHY/btslbxxgcrU/ZHNIEJQ9gKkEnMRmOnkXo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6pwHY/btslbxxgcrU/ZHNIEJQ9gKkEnMRmOnkXo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6pwHY%2FbtslbxxgcrU%2FZHNIEJQ9gKkEnMRmOnkXo1%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;1912&quot; height=&quot;1031&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;1031&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;2534&quot; data-origin-height=&quot;1241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLBRKC/btslhdrhpGQ/pRRarbQqXg4KcYdbaINndk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLBRKC/btslhdrhpGQ/pRRarbQqXg4KcYdbaINndk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLBRKC/btslhdrhpGQ/pRRarbQqXg4KcYdbaINndk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLBRKC%2FbtslhdrhpGQ%2FpRRarbQqXg4KcYdbaINndk%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;2534&quot; height=&quot;1241&quot; data-origin-width=&quot;2534&quot; data-origin-height=&quot;1241&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SpringBoot 프로젝트 구성&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;스프링 프로젝트를 다음과 같이 &lt;b&gt;Gradle 멀티모듈 프로젝트&lt;/b&gt;로 구성하였다. (이유는 나중에 설명)&lt;br /&gt;&lt;br /&gt;1. 가장 바깥쪽 껍데기 Root 프로젝트인 &lt;b&gt;remoteService&lt;/b&gt; 프로젝트 &lt;br /&gt;2. 요청을 받아 처리하는 서버 모듈인 &lt;b&gt;redissonServer&lt;/b&gt;&lt;br /&gt;3. 요청을 보내는 클라이언트 모듈인 &lt;b&gt;redissonClient&lt;/b&gt;&lt;br /&gt;4. Server 와 Client 가 공통적으로 사용하는 코드가 담인 공통묘듈 &lt;b&gt;redissonCommon&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCk5hg/btslpX8XO4u/MPI0nKQLIzg6GiiCj8hF60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCk5hg/btslpX8XO4u/MPI0nKQLIzg6GiiCj8hF60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCk5hg/btslpX8XO4u/MPI0nKQLIzg6GiiCj8hF60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCk5hg%2FbtslpX8XO4u%2FMPI0nKQLIzg6GiiCj8hF60%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;438&quot; height=&quot;309&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;426&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;611&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1nFO7/btslagJP5Qx/pe3471pjpksSep4KvkrWIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1nFO7/btslagJP5Qx/pe3471pjpksSep4KvkrWIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1nFO7/btslagJP5Qx/pe3471pjpksSep4KvkrWIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1nFO7%2FbtslagJP5Qx%2Fpe3471pjpksSep4KvkrWIk%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;375&quot; height=&quot;374&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;609&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;b&gt;remoteService&lt;/b&gt; &lt;/span&gt;- build.gradle &lt;br /&gt;루트 프로젝트에서 하위 서브모듈들의 공통적인 dependency 들을 정의해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713073064&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;buildscript {
	ext{
		springBootVersion = '3.1.0'
	}
	repositories {
		mavenCentral();
	}
	dependencies {
		classpath &quot;org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}&quot;
		classpath &quot;io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE&quot;
	}
}

// 하위 모든 프로젝트 공통 세팅
subprojects {
	repositories {
		mavenCentral()
	}
	apply plugin: 'java'
	apply plugin: 'idea'
	apply plugin: 'org.springframework.boot'
	apply plugin: 'io.spring.dependency-management'

	group 'com.example'
	version '1.0-SNAPSHOT'

	sourceCompatibility = '17'

	dependencies {
		compileOnly 'org.projectlombok:lombok'
		implementation 'org.springframework.boot:spring-boot-starter-data-redis'
		annotationProcessor 'org.projectlombok:lombok'
		annotationProcessor &quot;org.springframework.boot:spring-boot-configuration-processor&quot;
		testImplementation 'org.springframework.boot:spring-boot-starter-test'
	}

	tasks.named('test') {
		useJUnitPlatform()
	}
	tasks.register(&quot;prepareKotlinBuildScriptModel&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;b&gt;redissonServer&lt;/b&gt; &lt;/span&gt;- build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713157281&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bootJar {
    archiveFileName = 'redisson-client'
}

dependencies {
    implementation project(path: &quot;:redissonCommon&quot;, configuration: 'default')
    implementation('org.springframework.boot:spring-boot-starter-web')
}&lt;/code&gt;&lt;/pre&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 data-token-index=&quot;0&quot;&gt;&lt;b&gt;redissonCommon&lt;/b&gt; &lt;/span&gt;- build.gradle&lt;br /&gt;공통모듈은 독립으로 존재 하지않고 server 나 client 에 항상 종속적이므로 bootJar = false 로 함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713183352&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bootJar{enabled = false}
jar{enabled = true}

dependencies {
}&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;공통모듈 로드 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redissonCommon 프로젝트에 간단하게 ApplicationReadyEvent로 Member Entity 하나를 만들어서 redissonServer 모듈 실행 시 정상적으로 불러와지는지 확인해본다.&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;1227&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCDilR/btsla0GvMxY/eeRWKxneHfQERYKGJGgJR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCDilR/btsla0GvMxY/eeRWKxneHfQERYKGJGgJR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCDilR/btsla0GvMxY/eeRWKxneHfQERYKGJGgJR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCDilR%2Fbtsla0GvMxY%2FeeRWKxneHfQERYKGJGgJR0%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;698&quot; height=&quot;310&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;545&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;2266&quot; data-origin-height=&quot;1246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yohtC/btslffbNF6A/KeedYeDKJqsUkaeJZmM3KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yohtC/btslffbNF6A/KeedYeDKJqsUkaeJZmM3KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yohtC/btslffbNF6A/KeedYeDKJqsUkaeJZmM3KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyohtC%2FbtslffbNF6A%2FKeedYeDKJqsUkaeJZmM3KK%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;865&quot; height=&quot;476&quot; data-origin-width=&quot;2266&quot; data-origin-height=&quot;1246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redisson Common Module 작업하기&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vwid2/btsldQb1tzo/BSeFBp9gi6sYZ3eHUv1ojk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vwid2/btsldQb1tzo/BSeFBp9gi6sYZ3eHUv1ojk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vwid2/btsldQb1tzo/BSeFBp9gi6sYZ3eHUv1ojk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvwid2%2FbtsldQb1tzo%2FBSeFBp9gi6sYZ3eHUv1ojk%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;371&quot; height=&quot;412&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 추가한 Member 엔티티를 인자로 받아 이름을 바꿔주는 서비스인 RemoteMemberService의 인터페이스를 하나 만들겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;RemoteMemberServiceInterface.java&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713318409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.redissoncommon.remote;


import com.example.redissoncommon.entity.Member;

public interface RemoteMemberServiceInterface {

    public void renameMember(Member member, String newName);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redisson Remote Service 를 이용하기 위해선 Server 와 Client 가 같이 사용할 수 있는 인터페이스가 필요한데 Client쪽에서는 이 &lt;b&gt;RemoteMemberServiceInterface &lt;/b&gt;를 호출하는 방식으로 사용할 것이고 , Server 쪽에서는 &lt;b&gt;RedissonRemoteService 에 이 인터페이스와 인터페이스를 구현한 서비스 클래스를 등록&lt;/b&gt;할 것이다.&lt;/blockquote&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;Reddisson Server Module 작업하기&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu1SwI/btslqiL1JoM/ELU2u8WbDQhq8Jc2VtsDu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu1SwI/btslqiL1JoM/ELU2u8WbDQhq8Jc2VtsDu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu1SwI/btslqiL1JoM/ELU2u8WbDQhq8Jc2VtsDu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu1SwI%2FbtslqiL1JoM%2FELU2u8WbDQhq8Jc2VtsDu1%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;374&quot; height=&quot;459&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;application.yml&lt;/span&gt;&lt;/b&gt; (redissonServer)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713415223&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 7071
spring:
  data:
    redis:
      port: 6379
      host: redis://localhost&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;RemoteMemberService&lt;/span&gt;&lt;/b&gt;.java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713448224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.redissonserver.service;

import com.example.redissoncommon.entity.Member;
import com.example.redissoncommon.remote.RemoteMemberServiceInterface;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class RemoteMemberService implements RemoteMemberServiceInterface {
    @Override
    public Member renameMember(Member member,String newName){
        log.info(&quot;before rename =&amp;gt; {} &quot;,member);
        member.setName(newName);
        log.info(&quot;after rename =&amp;gt; {}&quot;,member);
        return member;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;아까 공통모듈 패키지에서 만들어놓은 RemoteMemberServiceInterface 를 구현하는 클래스를 서버쪽 모듈에 추가한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RedissonConfiguration.java&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713499280&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.redissonserver.config;

import com.example.redissoncommon.remote.RemoteMemberServiceInterface;
import com.example.redissonserver.service.RemoteMemberService;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.redisson.Redisson;
import org.redisson.api.RRemoteService;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;

@Configuration
@EnableCaching
@RequiredArgsConstructor
public class RedissonConfiguration {

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

    @Value(&quot;${spring.data.redis.port}&quot;)
    private String REDIS_PORT;

    private final RemoteMemberService remoteMemberService;

    @Bean
    public RedissonClient redissonClient() {
        Config redisConfig = new Config();
        redisConfig.useSingleServer()
                .setAddress(REDIS_HOST + &quot;:&quot; + REDIS_PORT);
        return Redisson.create(redisConfig);
    }

    @EventListener(ApplicationReadyEvent.class)
    public void registerRemoteService(){
        RRemoteService remoteService = redissonClient().getRemoteService();
        remoteService.register(RemoteMemberServiceInterface.class,remoteMemberService);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;521&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bksDZ4/btslahhHWE1/vyzS9tkhC7ftIAboHReEW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bksDZ4/btslahhHWE1/vyzS9tkhC7ftIAboHReEW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bksDZ4/btslahhHWE1/vyzS9tkhC7ftIAboHReEW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbksDZ4%2FbtslahhHWE1%2FvyzS9tkhC7ftIAboHReEW0%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;521&quot; height=&quot;229&quot; data-origin-width=&quot;521&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기선 간단하게 RedisClient 에 필요한 설정들과 등록할 RemoteService 메서드를 WAS 가 실행될 때 등록되게 해놓았다.&lt;/p&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;Config&lt;/b&gt; 부분이다. Config 부분에서 싱글서버로 등록할 때 여러 서버 형태를 체크해서 이미 존재하는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다시피 서버형태는 총 4개이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;&lt;b&gt;클러스터&lt;/b&gt; 모드&lt;/span&gt; : 말 그대로 여러개의 Redis로 구성되어있는 &lt;b&gt;레디스 클러스터&lt;/b&gt;를 사용하는 경우이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjd3ig/btslb5tJExb/yicmdRYkXa6kkcN6ZthZIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjd3ig/btslb5tJExb/yicmdRYkXa6kkcN6ZthZIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjd3ig/btslb5tJExb/yicmdRYkXa6kkcN6ZthZIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjd3ig%2Fbtslb5tJExb%2FyicmdRYkXa6kkcN6ZthZIk%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;988&quot; height=&quot;210&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;210&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.&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;&lt;b&gt;센티넬&lt;/b&gt; 모드&lt;/span&gt; : 다운되었을 때 고가용성을위한 &lt;b&gt;센티넬 모드&lt;/b&gt;로 사용할 경우이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X49qI/btsljSmPXYy/LNwjn4nvTkrR9T63LuFmsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X49qI/btsljSmPXYy/LNwjn4nvTkrR9T63LuFmsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X49qI/btsljSmPXYy/LNwjn4nvTkrR9T63LuFmsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX49qI%2FbtsljSmPXYy%2FLNwjn4nvTkrR9T63LuFmsK%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;983&quot; height=&quot;222&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;222&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.&lt;b&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;마스터 슬레이브 모드&lt;/span&gt;&lt;/b&gt; : &lt;b&gt;마스터-슬레이브 노드로 구성된 레디스 구성&lt;/b&gt;일 경우이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3lkKP/btslghNXFEE/jCKaYqx8yspG8TB7MZBDbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3lkKP/btslghNXFEE/jCKaYqx8yspG8TB7MZBDbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3lkKP/btslghNXFEE/jCKaYqx8yspG8TB7MZBDbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3lkKP%2FbtslghNXFEE%2FjCKaYqx8yspG8TB7MZBDbK%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;985&quot; height=&quot;224&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;224&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;4.&lt;b&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;싱글 서버 모드&lt;/span&gt;&lt;/b&gt; : 레디스 한 대로 모든 요청을 받는 용도로 사용할 경우 이다. 본 예제에서 사용할 모드이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqlgyU/btslaGIhUfK/66f3kl54cJ6PSeXHi1hmW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqlgyU/btslaGIhUfK/66f3kl54cJ6PSeXHi1hmW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqlgyU/btslaGIhUfK/66f3kl54cJ6PSeXHi1hmW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqlgyU%2FbtslaGIhUfK%2F66f3kl54cJ6PSeXHi1hmW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;586&quot; height=&quot;172&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;172&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;&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;634&quot; data-origin-height=&quot;26&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br33vL/btsljhtopnF/QKjVeIdNH0fcxzCMwpameK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br33vL/btsljhtopnF/QKjVeIdNH0fcxzCMwpameK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br33vL/btsljhtopnF/QKjVeIdNH0fcxzCMwpameK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr33vL%2FbtsljhtopnF%2FQKjVeIdNH0fcxzCMwpameK%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;634&quot; height=&quot;26&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;26&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 특이사항은 &lt;b&gt;RRemoteService&lt;/b&gt; 에다가 등록할 RemoteService 를 등록하는 과정인데 &lt;u&gt;&lt;b&gt;기본 값은&lt;/b&gt; &lt;span data-token-index=&quot;3&quot;&gt;요청이 들어올 때 하나의 스레드로만 처리하게&lt;/span&gt; 하는 옵션&lt;/u&gt;을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;span data-token-index=&quot;1&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b561bo/btslauaitIW/Hqqzfi7XbTKrF6RoA293AK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b561bo/btslauaitIW/Hqqzfi7XbTKrF6RoA293AK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b561bo/btslauaitIW/Hqqzfi7XbTKrF6RoA293AK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb561bo%2FbtslauaitIW%2FHqqzfi7XbTKrF6RoA293AK%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;761&quot; height=&quot;32&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;32&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 workerAmount 수를 지정해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 별도의 ExcutorService 를 등록하고 싶을 경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cceVRu/btslgisyVcd/KOMIU3BgXb4dy11p1IRZD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cceVRu/btslgisyVcd/KOMIU3BgXb4dy11p1IRZD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cceVRu/btslgisyVcd/KOMIU3BgXb4dy11p1IRZD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcceVRu%2FbtslgisyVcd%2FKOMIU3BgXb4dy11p1IRZD0%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;750&quot; height=&quot;32&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;32&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;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;클라이언트쪽에서는 간단하게 Member 객체를 생성하는 부분과 이름을 바꾸는 부분이 있는 Service 클래스와 요청을 받는 Controller 로 구성하였다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;679&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oN53f/btsldQiNOvF/k00jzBC2Mn1eG3NLNycljk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oN53f/btsldQiNOvF/k00jzBC2Mn1eG3NLNycljk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oN53f/btsldQiNOvF/k00jzBC2Mn1eG3NLNycljk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoN53f%2FbtsldQiNOvF%2Fk00jzBC2Mn1eG3NLNycljk%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;412&quot; height=&quot;473&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;679&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;application.yml&lt;/span&gt; &lt;/b&gt;(redissonClient)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713788684&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 7070
spring:
  data:
    redis:
      port: 6379
      host: redis://localhost&lt;/code&gt;&lt;/pre&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 data-token-index=&quot;0&quot;&gt;&lt;b&gt;RedissonConfiguration&lt;/b&gt;.java&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713814658&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.redissonclient.config;

import com.example.redissoncommon.remote.RemoteMemberServiceInterface;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.api.RemoteInvocationOptions;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class RedissonConfiguration {

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

    @Value(&quot;${spring.data.redis.port}&quot;)
    private String REDIS_PORT;

    @Bean
    public RedissonClient redissonClient() {
        Config redisConfig = new Config();
        redisConfig.useSingleServer()
                .setAddress(REDIS_HOST + &quot;:&quot; + REDIS_PORT);
        return Redisson.create(redisConfig);
    }

    @Bean
    public RemoteMemberServiceInterface remoteMemberService(){
        // 호출할 RemoteMemberService 를 RedisClient 로 부터 호출 하는 부분
        return redissonClient()
                .getRemoteService()
                .get(RemoteMemberServiceInterface.class, RemoteInvocationOptions.defaults()
//                        .noAck().noResult() // 변환 결과가 없을 경우 추가할 수 있는 옵션. blocking 이 걸리지 않는다.
                );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RemoteMemberService 를 Common 모듈의 인터페이스를 이용하여 &lt;span data-token-index=&quot;1&quot;&gt;&lt;b&gt;Bean 으로 설정과 동시에 컨테이너에 올리도록&lt;/b&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 data-token-index=&quot;0&quot;&gt;RemoteService&lt;/span&gt; &lt;/b&gt;를 호출할 때 &lt;b&gt;기본 값은 결과 값을 반환한다는 가정&lt;/b&gt;에 있다. 만약 반환 값이 있다면 추가로 설정해 줘야 하는것이 시간 관련된 부분이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sD3RK/btslasQ2GSA/5BOmK9Oc2sWjQXsx2lV2t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sD3RK/btslasQ2GSA/5BOmK9Oc2sWjQXsx2lV2t0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sD3RK/btslasQ2GSA/5BOmK9Oc2sWjQXsx2lV2t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsD3RK%2FbtslasQ2GSA%2F5BOmK9Oc2sWjQXsx2lV2t0%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;641&quot; height=&quot;135&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없다면 noAck() , noResult() 로 설정해 주면 된다.&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 data-token-index=&quot;0&quot;&gt;&lt;b&gt;MemberService&lt;/b&gt;.java&lt;/span&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713886484&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.redissonclient.service;

import com.example.redissoncommon.entity.Member;
import com.example.redissoncommon.remote.RemoteMemberServiceInterface;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Service
@RequiredArgsConstructor
@Slf4j
public class MemberService {

    private final AtomicLong memberIdSequence = new AtomicLong(0L);
    private final ConcurrentHashMap&amp;lt;Long,Member&amp;gt; memberDB = new ConcurrentHashMap&amp;lt;Long,Member&amp;gt;();

    private final RemoteMemberServiceInterface remoteMemberServiceInterface;

    public Member callRemoteRenameService(Member member,String name){

        if(member==null)
            throw new IllegalArgumentException(&quot;couldn't find member&quot;);
        return remoteMemberServiceInterface.renameMember(member,name);
    }

    public Member findMemberById(Long id){
        return Optional.ofNullable(memberDB.get(id))
                .orElse(null);
    }

    public Member createMember(String name){
        Member member = new Member(incrementSequence(), name);
        memberDB.put(member.getId(),member);
        return member;
    }

    private Long incrementSequence(){
        return memberIdSequence.incrementAndGet();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한부분은 RemoteMemberServiceInterface 인터페이스의 메서드를 호출한다는 것이다.&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 data-token-index=&quot;0&quot;&gt;&lt;b&gt;MemberController&lt;/b&gt;.java &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687713909034&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.redissonclient.controller;

import com.example.redissonclient.service.MemberService;
import com.example.redissoncommon.entity.Member;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
@Slf4j
@RequestMapping(&quot;/member&quot;)
public class MemberController {

    private final MemberService memberService;

    @PostMapping(&quot;/create&quot;)
    public Member createMember(@RequestParam String name){
        Member member = memberService.createMember(name);
        log.info(&quot;created member =&amp;gt; {}&quot;,member);
        return member;
    }

    @PostMapping(&quot;/rename&quot;)
    public Member renameMember(@RequestParam Long id,@RequestParam String name){
        Member findMember = memberService.findMemberById(id);
        return memberService.callRemoteRenameService(findMember,name);
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Postman 으로 요청 테스트&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1901&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/burlKb/btslbRoW8Rn/KRrEtDSXeDwNL8lHFKP2M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/burlKb/btslbRoW8Rn/KRrEtDSXeDwNL8lHFKP2M1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/burlKb/btslbRoW8Rn/KRrEtDSXeDwNL8lHFKP2M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FburlKb%2FbtslbRoW8Rn%2FKRrEtDSXeDwNL8lHFKP2M1%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;1901&quot; height=&quot;697&quot; data-origin-width=&quot;1901&quot; data-origin-height=&quot;697&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;1903&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/osH9p/btslaG9kTzV/hRTKoyUwhXObkOpIF9OWHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/osH9p/btslaG9kTzV/hRTKoyUwhXObkOpIF9OWHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/osH9p/btslaG9kTzV/hRTKoyUwhXObkOpIF9OWHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FosH9p%2FbtslaG9kTzV%2FhRTKoyUwhXObkOpIF9OWHK%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;1903&quot; height=&quot;788&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터로 간단하게 hello 를 전달하여 새로운 Memer 객체를 생성한 후 newHello 로 이름을 바꾸는 테스트이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1809&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K57jB/btslpWa9AIL/k7WwsTb9ytiXSXv3OwMp2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K57jB/btslpWa9AIL/k7WwsTb9ytiXSXv3OwMp2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K57jB/btslpWa9AIL/k7WwsTb9ytiXSXv3OwMp2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK57jB%2FbtslpWa9AIL%2Fk7WwsTb9ytiXSXv3OwMp2K%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;1809&quot; height=&quot;69&quot; data-origin-width=&quot;1809&quot; data-origin-height=&quot;69&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;&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;RemoteService 동작 살펴보기&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcsg1N/btslb5gernJ/y94Go6ZIYMYYRhkup3Dlc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcsg1N/btslb5gernJ/y94Go6ZIYMYYRhkup3Dlc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcsg1N/btslb5gernJ/y94Go6ZIYMYYRhkup3Dlc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcsg1N%2Fbtslb5gernJ%2Fy94Go6ZIYMYYRhkup3Dlc1%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;1312&quot; height=&quot;226&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈으로 올라와있는 인터페이스를 찍어보면 당연히 &lt;b&gt;jdk 동적 프록시&lt;/b&gt;를 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 빈 컨테이너로 올릴 때 인터페이스를 이용하면 jdk 동적 프록시를 사용하고 class 그대로 이용하면 aop 가 동작해 &lt;b&gt;CGLIB&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;인터페이스 메서드를 사용하는 부분에 debug 를 찍어보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uOYIR/btslpVQQK4V/JKlyboBWvHO68TzZjjkGqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uOYIR/btslpVQQK4V/JKlyboBWvHO68TzZjjkGqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uOYIR/btslpVQQK4V/JKlyboBWvHO68TzZjjkGqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuOYIR%2FbtslpVQQK4V%2FJKlyboBWvHO68TzZjjkGqK%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;1423&quot; height=&quot;545&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;InvocationalHandler &lt;/span&gt;가 동작하여 remoteInterface 의 getName()을 찾아 맞는 메서드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 getRequestQueueName 을 호출하는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJiSiN/btslpVXCMuk/JkfWoqikX7qhswqJ6YBZl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJiSiN/btslpVXCMuk/JkfWoqikX7qhswqJ6YBZl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJiSiN/btslpVXCMuk/JkfWoqikX7qhswqJ6YBZl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJiSiN%2FbtslpVXCMuk%2FJkfWoqikX7qhswqJ6YBZl0%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;613&quot; height=&quot;89&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이부분. ConcurrentCacheMap 으로 만들어진 요청큐 안에 특별한 키 값으로 요청을 넣는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIysDZ/btslcCkPLx9/yfKt9h0xmdFKpWt4K7w0fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIysDZ/btslcCkPLx9/yfKt9h0xmdFKpWt4K7w0fK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIysDZ/btslcCkPLx9/yfKt9h0xmdFKpWt4K7w0fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIysDZ%2FbtslcCkPLx9%2FyfKt9h0xmdFKpWt4K7w0fK%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;916&quot; height=&quot;49&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;49&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;952&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhpLwn/btslb5mZDcD/NkS1kkQfODnKt0yYsfxnhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhpLwn/btslb5mZDcD/NkS1kkQfODnKt0yYsfxnhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhpLwn/btslb5mZDcD/NkS1kkQfODnKt0yYsfxnhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhpLwn%2Fbtslb5mZDcD%2FNkS1kkQfODnKt0yYsfxnhK%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;952&quot; height=&quot;60&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해 보니 &lt;span data-token-index=&quot;1&quot;&gt;{redisson_rs : ~~~ }&lt;/span&gt; 형태로 들어간다. 직접 확인해보기 위해 RemoteServer를 잠시 꺼보자. 그리고 Redis UI tool 에서 확인해보면&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;1373&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kFST9/btslbQRacQm/S9TxMN2onTjtWIjD57GKM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kFST9/btslbQRacQm/S9TxMN2onTjtWIjD57GKM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kFST9/btslbQRacQm/S9TxMN2onTjtWIjD57GKM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkFST9%2FbtslbQRacQm%2FS9TxMN2onTjtWIjD57GKM1%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;1373&quot; height=&quot;202&quot; data-origin-width=&quot;1373&quot; data-origin-height=&quot;202&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;1360&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KahUm/btslmTscVs0/BCL0dAPxxAvNezCsbjFME1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KahUm/btslmTscVs0/BCL0dAPxxAvNezCsbjFME1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KahUm/btslmTscVs0/BCL0dAPxxAvNezCsbjFME1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKahUm%2FbtslmTscVs0%2FBCL0dAPxxAvNezCsbjFME1%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;1360&quot; height=&quot;559&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;559&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;1317&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM9cvL/btslhdks3N3/oKilvKlOKBegVjfpoGiqd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM9cvL/btslhdks3N3/oKilvKlOKBegVjfpoGiqd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM9cvL/btslhdks3N3/oKilvKlOKBegVjfpoGiqd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM9cvL%2Fbtslhdks3N3%2FoKilvKlOKBegVjfpoGiqd0%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;1317&quot; height=&quot;470&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요런 모양으로 들어와 있는데 잘 보면 Key 값이 아까 본 모양이랑 똑같이 나와있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;즉, 내부적으로 Key 를 생성할 때 결정자 중 하나가 &amp;ldquo;패키지 경로&amp;rdquo; 라는 것.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제로 value 로 나와있는 저 값들도 다시 디코딩을 해보면 호출하는 메서드의 패키지 위치라던지 필요한 파라미터 또한 패키지경로 까지 포함한 값으로 들어오게 되어있다.&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;그래서 처음에 구조 설계를 할때 Common 공통 모듈을 생성한 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 와 Client 가 공통적으로 사용해야하는 &lt;b&gt;RemoteMemberServiceInterface&lt;/b&gt; 가 Common 모듈 안에 있어야 RemoteServer 가 요청을 받은 후 에도 해당 패키지로 찾아가서 해당 메서드를 Call 할 수 있기 때문. 즉 , &lt;u&gt;&lt;b&gt;공통 모듈 구조가 강제화 된다는 뜻이다&lt;/b&gt;.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 가장 큰 단점이라 만약 사용한다면 저 &lt;b&gt;Common 모듈을 원격 전용 모듈로서 따로 분리&lt;/b&gt;하고 파라미터로 받는 Member 객체도 엔티티 그대로 사용하지 말고 별도의 Remote 전용 MemberDto 로 따로 컨버팅하고 사용하야 구조에 지나치게 의존적인 관계가 그나마 덜 해지는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, Redisson Config을 설정할 때 내부적으로 Serialier 를 &lt;b&gt;Kyro&lt;/b&gt; 를 사용하고있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWzeDk/btslbx4674w/sxqsgG4WoeIIoySZfIuz8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWzeDk/btslbx4674w/sxqsgG4WoeIIoySZfIuz8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWzeDk/btslbx4674w/sxqsgG4WoeIIoySZfIuz8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWzeDk%2Fbtslbx4674w%2FsxqsgG4WoeIIoySZfIuz8K%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;454&quot; height=&quot;245&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 Codec을 &lt;b&gt;JsonJacksonCodec&lt;/b&gt; 으로 변경해서 데이터를 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mby4h/btslaGuGOjD/dbZ57e7xkQm8qz5ELzBOVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mby4h/btslaGuGOjD/dbZ57e7xkQm8qz5ELzBOVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mby4h/btslaGuGOjD/dbZ57e7xkQm8qz5ELzBOVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmby4h%2FbtslaGuGOjD%2FdbZ57e7xkQm8qz5ELzBOVk%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;514&quot; height=&quot;207&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;276&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;1313&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4B7ME/btslasXRaji/HZFFOABYSxtKqPGKwAHBP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4B7ME/btslasXRaji/HZFFOABYSxtKqPGKwAHBP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4B7ME/btslasXRaji/HZFFOABYSxtKqPGKwAHBP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4B7ME%2FbtslasXRaji%2FHZFFOABYSxtKqPGKwAHBP0%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;1313&quot; height=&quot;441&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다시피 파라미터로 받는 Member 엔티티의 클래스 경로까지 모두 포함한다는 것이 보인다. 즉 , 도메인 주도 설계를 한 경우엔 쓰기 쉽지 않아 보이는 강제된 설계구조란 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로 Serializer 를 재 정의 하거나, 혹은 한다 하더라도 어떤 예상치 못한 문제가 발생할 지 알 수 없으므로 도입하기 애매한 기술인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 만약 사용한다면 중요 트랜잭션로직의 속도를 중시한다면 인 메모리 기반 RemoteService 이므로 사용해도 괜찮겠지만 그게 아니라면 별도의 MessageQueue 를 이용하는 것이 좋아보인다.&lt;/p&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/nicebyy/RedissonRemoteService&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/nicebyy/RedissonRemoteService&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687714402974&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - nicebyy/RedissonRemoteService&quot; data-og-description=&quot;Contribute to nicebyy/RedissonRemoteService development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nicebyy/RedissonRemoteService&quot; data-og-url=&quot;https://github.com/nicebyy/RedissonRemoteService&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AcB2C/hyS6Id3uG2/MsTj9PQgLwVNWTfGOhkXFK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nicebyy/RedissonRemoteService&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nicebyy/RedissonRemoteService&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AcB2C/hyS6Id3uG2/MsTj9PQgLwVNWTfGOhkXFK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - nicebyy/RedissonRemoteService&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to nicebyy/RedissonRemoteService development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>MSA</category>
      <category>Redis</category>
      <category>Redisson</category>
      <category>RedissonRemoteService</category>
      <category>RemoteService</category>
      <category>레디스</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/179</guid>
      <comments>https://katastrophe.tistory.com/179#entry179comment</comments>
      <pubDate>Sun, 25 Jun 2023 21:51:15 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 동시성 관리와 락 병행제어 (Redisson, Mysql 네임드락)</title>
      <link>https://katastrophe.tistory.com/172</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;정말 오랜만에 블로그를 쓰는 것 같다. 쓰고 싶은 것들이 한 트럭인데 생각만하다가 흘러간 세월이 벌써 이렇게된듯..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;틈틈이 이제 써 봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 병행제어&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;실무에서 동시성관리는 매우 중요하다. 특히나 내가 속한 팀인 거래소팀에서는 더욱 중요한 이슈이다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;이번에 하나의 &lt;span style=&quot;color: #ee2323;&quot;&gt;Redis 서버에서 과부하가 자주 일어나서 개선하는 방향&lt;/span&gt;으로 작업을 하다보니 거래소 차트그리는 쪽을 손보게 되었다. 밑의 그림은 대형 거래소 차트 예시이다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvTYVM/btr8u9otlUg/KiPgzrUfGdzKWXFbYMakL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvTYVM/btr8u9otlUg/KiPgzrUfGdzKWXFbYMakL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvTYVM/btr8u9otlUg/KiPgzrUfGdzKWXFbYMakL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvTYVM%2Fbtr8u9otlUg%2FKiPgzrUfGdzKWXFbYMakL0%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;600&quot; height=&quot;253&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;620&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;우리거래소는 코인 페어별로 (USDT, ETH, BTC) 등 차트를 그리는데 아무튼 각설하고 , 대충 차트를 그리는 서버가 여러개란 뜻이다. 각 서버는 1분 간격 스케쥴러로 동작하며, 차트를 그린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차트그리기 전용 데이터를 다루는 DB가 별도로 존재하며, 차트를 그리기위해 DB값을 업데이트하고 웹소켓을 이용하여 프론트단으로 데이터를 보내 실시간으로 시세가 변하는 것을 보여주기 위함이고, &lt;b&gt;여러개의 서버&lt;/b&gt;가 차트를 중복해서 그리거나 데이터를 보내면 안되므로 &lt;b&gt;병행제어가 필요했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성관리를 위한 방법들은 여러개 있는데, JAVA 의 &lt;b&gt;syncronized&lt;/b&gt; 메서드 블록을 이용한다던가 &lt;b&gt;Atomic&lt;/b&gt; 타입을 사용해서 해결 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;이 방법은 같은 프로세스 내 에서 다른 스레드끼리 경쟁할때만 유효하고 애초에 서버가 다르면 결국 똑같은 문제를 발생시킨다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;병행제어를 위한 분산락&lt;/b&gt;이 필요한데 두 가지 방법을 소개하려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 분산락&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;분산락은 대표적으로 &lt;u&gt;Redis 나 주키퍼 , Mysql의 NamedLock&lt;/u&gt; 등이 있는데 기존엔 Redisson 클라이언트를 이용한 Lock 제어를 하였다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;[1] Redisson&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1435&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCFyRB/btr8u8pxyNp/ahauPy4JTUk5hSO4kUHVmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCFyRB/btr8u8pxyNp/ahauPy4JTUk5hSO4kUHVmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCFyRB/btr8u8pxyNp/ahauPy4JTUk5hSO4kUHVmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCFyRB%2Fbtr8u8pxyNp%2FahauPy4JTUk5hSO4kUHVmk%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;1435&quot; height=&quot;489&quot; data-origin-width=&quot;1435&quot; data-origin-height=&quot;489&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Redis 클라이언트는 분산락을 이용하기위해 Redisson을 이용한 상태이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 원하는 key 값으로 락을걸면 정한 시간이나 동작이 완료될 때 까지 Redis 안에 key 값으로 락이 걸리게 되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 서버들은 Redis에 접근할 때 이미 락이 걸려있기 때문에 중복작업을 하지 않게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPiBKK/btr8zlOWSpt/NK6V8TLr46QFnRkN8nSZx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPiBKK/btr8zlOWSpt/NK6V8TLr46QFnRkN8nSZx1/img.png&quot; data-alt=&quot;Redis 내부 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPiBKK/btr8zlOWSpt/NK6V8TLr46QFnRkN8nSZx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPiBKK%2Fbtr8zlOWSpt%2FNK6V8TLr46QFnRkN8nSZx1%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;300&quot; height=&quot;437&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Redis 내부 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아까 말했듯이 ( Redis를 이용한 원격 함수호출요청이 몇십만, 백만 까지 넘쳐나는 등 .. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt; Redis 과부하를 줄이기 위한것이 목적이었기 때문에, 그리고 또 다른 문제점이 발견되어 Redisson 락을 버리기로 하였다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Redis의 락은&lt;span style=&quot;color: #ee2323;&quot;&gt; Redis Transaction 행위&lt;/span&gt; 중 하나이고, 이는 connection을 계속 점유한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 다른 작업의 connection 때문에 락을 획득하기위한 connection을 실패하게 되어&lt;span style=&quot;color: #ee2323;&quot;&gt; 락 획득 자체가 실패&lt;/span&gt;한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQgts7/btr8tCY2p33/ShPg30cRP1hDKK2EvReABK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQgts7/btr8tCY2p33/ShPg30cRP1hDKK2EvReABK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQgts7/btr8tCY2p33/ShPg30cRP1hDKK2EvReABK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQgts7%2Fbtr8tCY2p33%2FShPg30cRP1hDKK2EvReABK%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;300&quot; height=&quot;152&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드와 같이 &lt;b&gt;한정된 connectionPoolSize 에 비해 차트를 그리는서버는 총 4개.. 즉 , 락을 잡고 처리하는데 병목이 발생&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기본적으로 Redis는 싱글스레드 기반이라 별 상관없다고 생각할 수 있겠지만 유일하게 Connection에 영향을 끼치는게 &lt;b&gt;Redis Transaction&lt;/b&gt;이다. ( Redis Transaction 참고 : &lt;a href=&quot;https://sabarada.tistory.com/177&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sabarada.tistory.com/177&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;락을 거는 행위는 Redis 의 WATCH 명령에 속하고 커넥션을 점유하게 되어 병목을 유발 시키거나 , Redis 의 다른 작업이 처리되느라 락 획득에 실패하는 경우가 발생하여서 다른 분산락을 이용하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;[2] MySQL NamedLock&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 DB도 mysql 5.7 이상 버전을 사용하고 있었고, Redis 와 같은 인프라 관련 유지보수 비용도 만만치 않은 상태인데다가 우형 기술블로그가 도움이 많이 되어서 &lt;b&gt;NamedLock&lt;/b&gt;을 이용하기로 하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( 참고 : &lt;a href=&quot;https://techblog.woowahan.com/2631/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://techblog.woowahan.com/2631/&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;857&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhHzFy/btr8xODQZhO/KADzPn3KqNt9Pq5qmavxXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhHzFy/btr8xODQZhO/KADzPn3KqNt9Pq5qmavxXk/img.png&quot; data-alt=&quot;mysql NamedLock 으로 변경 후 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhHzFy/btr8xODQZhO/KADzPn3KqNt9Pq5qmavxXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhHzFy%2Fbtr8xODQZhO%2FKADzPn3KqNt9Pq5qmavxXk%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;700&quot; height=&quot;500&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;857&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mysql NamedLock 으로 변경 후 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyr3dY/btr8w6Y6bsu/EtFkGPrslHi4P9evOf5Nqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyr3dY/btr8w6Y6bsu/EtFkGPrslHi4P9evOf5Nqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyr3dY/btr8w6Y6bsu/EtFkGPrslHi4P9evOf5Nqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdyr3dY%2Fbtr8w6Y6bsu%2FEtFkGPrslHi4P9evOf5Nqk%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;1604&quot; height=&quot;229&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;229&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;1209&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cApTBJ/btr8xO4TMjt/z09KRCpMjlHVGQNpjp4Phk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cApTBJ/btr8xO4TMjt/z09KRCpMjlHVGQNpjp4Phk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cApTBJ/btr8xO4TMjt/z09KRCpMjlHVGQNpjp4Phk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcApTBJ%2Fbtr8xO4TMjt%2Fz09KRCpMjlHVGQNpjp4Phk%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;700&quot; height=&quot;126&quot; data-origin-width=&quot;1209&quot; data-origin-height=&quot;218&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;b&gt;중요한 곳은 NamedLock과 관련된 chartLockRepository 이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LCWYF/btr8zlnRIA2/eKawd1Kou0W6YNA0NwalZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LCWYF/btr8zlnRIA2/eKawd1Kou0W6YNA0NwalZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LCWYF/btr8zlnRIA2/eKawd1Kou0W6YNA0NwalZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLCWYF%2Fbtr8zlnRIA2%2FeKawd1Kou0W6YNA0NwalZK%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;600&quot; height=&quot;263&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, &lt;b&gt;JPA에서는 공식적으로 NamedLock 을 제공하지 않기때문에&lt;/b&gt; native query를 이용해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(관련 공식 레퍼런스 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html&quot;&gt;https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySql 의 네임드 락 관련 함수는 총 세 가지를 사용했는데 ,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 락이 있는지 확인하는 쿼리 : select is_used_lock(key)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 락을 획득하는 쿼리 : select get_lock(key, time)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 락을 반환하는 쿼리 : select release_lock(key)&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;1 -&amp;gt; 2 -&amp;gt; 3 순서대로 락 관련 제어를 하였다.&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;이렇게 방식을 바꾸고 나니, &lt;b&gt;&lt;u&gt;락을 획득하는데 실패하는 경우가 이젠 더이상 나오지 않은&lt;/u&gt;데다가 &lt;u&gt;Redis 의존을 떼어내어 부하를 조금 줄이게 되었다&lt;/u&gt;&lt;/b&gt;. 중간에 &lt;b&gt;@Transactional 에 전파옵션을 Required_new 로 지정해 논 곳이 있는데 , 배치 작업을 하다 중간에 실패해도 해당 코인 외 나머지 차트그리는 작업까지 롤백이 되면 안되므로 전파옵션도 바꾸게 되었다.&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;아래는 GPT 한테 도움받은 몇가지 질문들&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;1011&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QZUB6/btr8Be9Q3X4/M4sqMaYZjCCKJ5KKk7FUT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QZUB6/btr8Be9Q3X4/M4sqMaYZjCCKJ5KKk7FUT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QZUB6/btr8Be9Q3X4/M4sqMaYZjCCKJ5KKk7FUT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQZUB6%2Fbtr8Be9Q3X4%2FM4sqMaYZjCCKJ5KKk7FUT1%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;600&quot; height=&quot;504&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;1011&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;1245&quot; data-origin-height=&quot;719&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xwfNR/btr8ndkZ0tJ/RofGCk0ri6XG2jJ2mqiQhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xwfNR/btr8ndkZ0tJ/RofGCk0ri6XG2jJ2mqiQhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xwfNR/btr8ndkZ0tJ/RofGCk0ri6XG2jJ2mqiQhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxwfNR%2Fbtr8ndkZ0tJ%2FRofGCk0ri6XG2jJ2mqiQhK%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;600&quot; height=&quot;347&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;719&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;1233&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NeDPs/btr8u9PwDcR/ZPLDe6g8d7IekDLp9DrkpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NeDPs/btr8u9PwDcR/ZPLDe6g8d7IekDLp9DrkpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NeDPs/btr8u9PwDcR/ZPLDe6g8d7IekDLp9DrkpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNeDPs%2Fbtr8u9PwDcR%2FZPLDe6g8d7IekDLp9DrkpK%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;600&quot; height=&quot;396&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반박시 님말 맞음....!!&lt;/p&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;i&gt;&lt;s&gt;아 .. 팀장님좀 뽑아줬으면.. 잘 하고 있는건지 모르겠다..&amp;nbsp;&lt;/s&gt;&lt;/i&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;그런데, 분명 1분동안 락을 점유하도록 했는데 처리시간이 1분이상 걸린건 뭔지 모르겠다.. 아시는 분 댓글부탁드려요..ㅠ&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>MYSQL</category>
      <category>NamedLock</category>
      <category>Redis</category>
      <category>Redisson</category>
      <category>Spring</category>
      <category>네임드락</category>
      <category>레디스</category>
      <category>병행제어</category>
      <category>분산락</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/172</guid>
      <comments>https://katastrophe.tistory.com/172#entry172comment</comments>
      <pubDate>Fri, 7 Apr 2023 01:56:00 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Spring Redirect 가 동작하지 않는 이유</title>
      <link>https://katastrophe.tistory.com/170</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제상황&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/product URL로 상품 등록이라는 동작을 한 뒤에 내가 등록한 상품 리스트로 redirect 명령을 내리려고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통, 이런 리다이렉션 같은 경우는 상품을 등록 후 새로고침을 수행하였을때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재 등록을 방지하기 위해서 사용하는데 소위 PRG (Post, Redirect, Get 이라고 부른다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;1388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcYurE/btrQvYqXxYA/738wUXGdAhqiCivQ7DrLQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcYurE/btrQvYqXxYA/738wUXGdAhqiCivQ7DrLQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcYurE/btrQvYqXxYA/738wUXGdAhqiCivQ7DrLQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcYurE%2FbtrQvYqXxYA%2F738wUXGdAhqiCivQ7DrLQk%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;577&quot; height=&quot;722&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;1388&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_1667791858041&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @PostMapping(&quot;/new&quot;)
    @ResponseStatus(HttpStatus.CREATED)
    public String addProduct(
            @ModelAttribute ProductDto.CreateProduct createProduct,
            @AuthenticationPrincipal CustomUserDetails user,
            @RequestParam List&amp;lt;MultipartFile&amp;gt; productImages
    ) throws IOException {
        User currentUser = userService.findByUserId(user.getUserId());
        productService.registerProduct(currentUser,createProduct,productImages);

        return &quot;redirect:/product&quot;;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품 등록을 모두 수행한 후, 상품 리스트를 보여주는 Url (/product) 로 Get요청을 하는 상황.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품이 성공적으로 등록되었으면 201 상태코드를 반환을 동시에 수행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1510&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rwUOE/btrQCUHHRcu/UTLovc6bwGqctfqK5FhqkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rwUOE/btrQCUHHRcu/UTLovc6bwGqctfqK5FhqkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rwUOE/btrQCUHHRcu/UTLovc6bwGqctfqK5FhqkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrwUOE%2FbtrQCUHHRcu%2FUTLovc6bwGqctfqK5FhqkK%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;560&quot; height=&quot;313&quot; data-origin-width=&quot;1510&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 등록하기를 누르면 예상과 달리 리다이렉트를 수행하지 않는 문제의 상황이 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;37&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/77Kec/btrQyw8GDUI/ki2qbDSItUJ6S9enEjOsPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/77Kec/btrQyw8GDUI/ki2qbDSItUJ6S9enEjOsPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/77Kec/btrQyw8GDUI/ki2qbDSItUJ6S9enEjOsPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F77Kec%2FbtrQyw8GDUI%2Fki2qbDSItUJ6S9enEjOsPK%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;342&quot; height=&quot;37&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;37&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곰곰히 생각해보니 &lt;b&gt;Redirect 동작은 서버 =&amp;gt; 브라우저 에게 내리는 명령&lt;/b&gt;이므로 결국 수행은 브라우저가 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 기준은 Post 수행후 반환된 상태코드를 기준으로 판별한다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 변경한다. (HttpStatus.CREATED =&amp;gt;&amp;nbsp; HttpStatus.FOUND)&lt;/p&gt;
&lt;pre id=&quot;code_1667792289374&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @PostMapping(&quot;/new&quot;)
    @ResponseStatus(HttpStatus.FOUND)
    public String addProduct(
            @ModelAttribute ProductDto.CreateProduct createProduct,
            @AuthenticationPrincipal CustomUserDetails user,
            @RequestParam List&amp;lt;MultipartFile&amp;gt; productImages
    ) throws IOException {

        User currentUser = userService.findByUserId(user.getUserId());
        productService.registerProduct(currentUser,createProduct,productImages);

        return &quot;redirect:/product&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;다시 상품등록을 똑같이 요청하면 , 수행 후 정상적으로 redirect가 된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lSL4s/btrQtTcGGAz/qqy0gvTduCEi0b4hMknHEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lSL4s/btrQtTcGGAz/qqy0gvTduCEi0b4hMknHEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lSL4s/btrQtTcGGAz/qqy0gvTduCEi0b4hMknHEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlSL4s%2FbtrQtTcGGAz%2Fqqy0gvTduCEi0b4hMknHEk%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;1728&quot; height=&quot;621&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>Spring Redirect</category>
      <category>리다이렉션</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/170</guid>
      <comments>https://katastrophe.tistory.com/170#entry170comment</comments>
      <pubDate>Mon, 7 Nov 2022 12:41:59 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] WebFlux 도입에 관한 이야기</title>
      <link>https://katastrophe.tistory.com/169</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;WebFlux 란 웹에서&lt;u&gt; Reactor의 스트리밍 처리를 담당하기 위&lt;/u&gt;해 Spring 5.0 부터 도입이 되었고&lt;/b&gt;&lt;br /&gt;&lt;b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;non-blocking&lt;/span&gt; 에 기반한 reactive streams 를 back pressure 와 함께 처리하여 &lt;u&gt;동시에 많은 요청을 처리하기 위한 목적&lt;/u&gt;으로 나왔다.&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;이전의 Reactive streams는 low 레벨이기 때문에 API 에 있어서 유용하지 못했지만 Webflux에서 풍부한 라이브러리와 연산자들을 통해 서버사이드에 좀 더 집중할 수 있게 되었다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pxi7X/btrQgLfsedq/wXpNgkntdDbxOkHWsU06wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pxi7X/btrQgLfsedq/wXpNgkntdDbxOkHWsU06wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pxi7X/btrQgLfsedq/wXpNgkntdDbxOkHWsU06wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpxi7X%2FbtrQgLfsedq%2FwXpNgkntdDbxOkHWsU06wK%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;641&quot; height=&quot;499&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czSz32/btrQlj2Acyi/hAkaIDsaZH2rJBV9zDbMoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czSz32/btrQlj2Acyi/hAkaIDsaZH2rJBV9zDbMoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czSz32/btrQlj2Acyi/hAkaIDsaZH2rJBV9zDbMoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczSz32%2FbtrQlj2Acyi%2FhAkaIDsaZH2rJBV9zDbMoK%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;643&quot; height=&quot;359&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;578&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebFlux vs SpringMVC&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;기본적으로 SpringMVC는 멀티스레드에 기반한 명령형 프로그래밍 방식이다. &lt;/b&gt;&lt;br /&gt;&lt;b&gt;즉, 나타내고자 하는 기능을 분명하게 나눌 수 있고 디버깅이 쉬우며 유지보수가 편리하다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; 그에 반해 WebFlux는 여러 Streams 복잡한 조합과 비동기적인 함수형 프로그래밍 방식을 채택하여 파이프라인을 구성하는 것이기 때문에 변경 추적이 쉽지 않을 뿐더러 변경 추적이 어렵다. &lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1667469499514&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;basketFlux.concatMap(basket -&amp;gt; {
    final Mono&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; distinctFruits = Flux.fromIterable(basket).distinct().collectList();
    final Mono&amp;lt;Map&amp;lt;String, Long&amp;gt;&amp;gt; countFruitsMono = Flux.fromIterable(basket)
            .groupBy(fruit -&amp;gt; fruit)
            .concatMap(groupedFlux -&amp;gt; groupedFlux.count()
                .map(count -&amp;gt; {
                    final Map&amp;lt;String, Long&amp;gt; fruitCount = new LinkedHashMap&amp;lt;&amp;gt;();
                    fruitCount.put(groupedFlux.key(), count);
                    return fruitCount;
                }) 
            ) 
            .reduce((accumulatedMap, currentMap) -&amp;gt; new LinkedHashMap&amp;lt;String, Long&amp;gt;() { {
                putAll(accumulatedMap);
                putAll(currentMap);
            }})
    return Flux.zip(distinctFruits, countFruitsMono, (distinct, count) -&amp;gt; new FruitInfo(distinct, count));
}).subscribe(System.out::println);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Webflux 예시코드)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그럼에도 WebFlux를 쓰는이유&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;그렇다면 왜 굳이 SpringMVC대신 Webflux 를 쓰려고할까. &lt;br /&gt;단순히 많은 요청을 더 잘 처리하기 위해서 혹은 반전이 일어날만한 성능 기대를 위해 도입을 해야하나?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 초당 500개 정도의 요청을 받는다면 사용을 고려할 만하다고 하지만 여러 글들을 찾아 본 결과 다음과 같은 환경이면 도입할만 하다고 판단하는 것 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터를 스트리밍으로 지속적으로 보내줘야 하는 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대부분 비즈니스 로직이 다른 업스트림 호출이 반환되기를 기다리는 경우 (ex. 오케스트레이션, API 게이트 웨이)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;여러 API를 호출하는 로직 그리고 이를 위합하여 전달하는 시스템일 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빈번한 컨텍스트 스위칭으로 인해 늘어난 IO 수행시간으로 인해 CPU와 메모리에 부하가 심한경우 (무거운 스레드)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebFlux에 맞게 쓰려면&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;webflux는 기본적으로 비동기이기 때문에 그에 맞는 환경이 갖춰진 상태에서 도입을 해야 적절하게 잘 썼다 라고 얘기할 수 있을 것 같다. &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;예를들어 Tmax 사의 Tibero DB와 Rxjava, Webflux를 이용하여 교통정보를 실시간 스트림으로 보내주는 작업을 하는 친구가 있는데 DB가 계속 죽는다고 얘기를 했었다. &lt;/b&gt;&lt;br /&gt;&lt;b&gt;그 원인이 &lt;span style=&quot;color: #ee2323;&quot;&gt;기본적으로 RDB는 블로킹을 동반한 동기성 D&lt;/span&gt;B 이기 때문이다. &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;아무리 서버로직에서 비동기로 처리하더라도 DB가 비동기가 아니면 결국 병목&lt;/u&gt;은 똑같이 걸리기 때문.&lt;/b&gt;&lt;br /&gt;&lt;b&gt; 따라서 Webflux 를 사용하려면 이와 관련된 DB는 Redis 나 R2DBC와 같은 Nosql 기반 DB를 선택하는 것도 방법이 되겠다. &lt;/b&gt;&lt;br /&gt;&lt;b&gt;이러한 이유로 우리회사 같은경우 단순 호출을 기반으로 진행된 챗봇 프로젝트에선 webflux와 redis를 이용하여 개발을 했었다. (현재는 보류) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;https://www.youtube.com/watch?v=I0zMm6wIbRI&lt;/b&gt;&lt;br /&gt;&lt;b&gt;어느 머기업에서 Webflux를 도입했다가 오히려 느렸던 이유에 대해 설명해논 영상이있다. 블록킹코드가 하나라도 있으면 Webflux 도입하는 의미가 퇴색된다는 것이다. &lt;u&gt;즉 모든 코드들은 가능한 비동기 Non-blocking&lt;/u&gt;으로 개발해야 한다.&lt;/b&gt;&lt;/blockquote&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;1009&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uYx5X/btrQkChkZ0x/KMhjsTXwgg43zin5urXIjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uYx5X/btrQkChkZ0x/KMhjsTXwgg43zin5urXIjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uYx5X/btrQkChkZ0x/KMhjsTXwgg43zin5urXIjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuYx5X%2FbtrQkChkZ0x%2FKMhjsTXwgg43zin5urXIjK%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;679&quot; height=&quot;334&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>spring webflux</category>
      <category>Webflux</category>
      <category>webflux 도입</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/169</guid>
      <comments>https://katastrophe.tistory.com/169#entry169comment</comments>
      <pubDate>Thu, 3 Nov 2022 19:01:58 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Redis</title>
      <link>https://katastrophe.tistory.com/167</link>
      <description>&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/188285042-f917aefd-792f-407c-9f8e-5490d7ad321e.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Redis&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Remote Dictionary Server 의 약자로 In-Memory 기반 Key-Value 형식의 Cache Store 이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cach&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용하는 데이터나 값을 미리 복사해 놓는 임시 저장소이며 read-write 가 빈번하게 일어날 때 사용하며 저장공간이 적다는 것이 특징&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MemCache vs Redis&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션은 Memory 기반으로 동작해서 데이터를 저장하는 저장소인데 굳이 Redis를 사용해야 하는이유와 Spring 에서 공식적으로 Session 클러스터링을 Redis 방식을 선택하는 이유가 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지 공통점과 차이점을 비교하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/188285055-b19a3b70-b366-4086-9a5a-95f660840406.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가장 큰 특징&lt;/b&gt;은&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;다양한 자료구조&lt;/b&gt;를 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 저장할 때 Disk에도 저장이 가능&lt;/b&gt;할 수 있게 해 주기 때문에 복구에 용이&lt;/li&gt;
&lt;li&gt;Memory Cache는 LRU 방식으로 동작하지만 &lt;b&gt;Redis는 다양한 정책을 선택&lt;/b&gt;이 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis 적용 예시&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 를 사용하는 기준은 다르겠지만, 단순 캐싱용으로 사용하는 용도를 넘어서는 경우에 사용한다.&lt;br /&gt;예를들어 서버가 여러개일경우 사용자 로그인 정보를 서버끼리 서로 공유할 수 있는 형태로 있어야 하는데 이를 세션 클러스터링이라고 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/188285063-f6f02174-4329-4bef-87ae-99251845871a.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Server1에서 뭔가 요청을 처리하고 있고 , Server2 와 Server3도 다 같은 세션정보를 알고 있어야 한다고 가정을 하자. (ex. 로그인 된 사용자 정보)&lt;br /&gt;그럼 Server1는 Server2와 Server3에게 정보를 보내주면 Memory내에 사용자 정보를 읽어들여 세션을 유지해야하는데 이는 서버에게 부담을 초래 하게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis Session 클러스터링&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/188285068-c61a22bf-f76b-4dfd-83a4-4380de3cc95d.png&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 세션 전용 서버를 따로두면 Server2와 Server3은 별도의 세션 서버로부터 정보를 읽어 들일 수 있기 때문에 로그인이 유지된 채 사용자는 여전히 서비스를 이용가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 만약 어떤 쇼핑몰 시스템이 있고 도메인서버가 분리되어있을 경우 같은 사용자 정보를 이용해야 할 때 세션 클러스터링을 통해 사용자정보를 받아올 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 Redis UI 예시를 보면 현재 로그인 된 사용자 세션을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/188285071-6897cdb7-92ed-4402-9051-b76ac1b65c88.png&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Spring 에서 Redis 이용해보기&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공식문서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Download : &lt;a href=&quot;https://redis.io/download/&quot;&gt;https://redis.io/download/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis UI : &lt;a href=&quot;https://www.electronjs.org/apps/p3x-redis-ui&quot;&gt;https://www.electronjs.org/apps/p3x-redis-ui&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Redis Doc : &lt;a href=&quot;https://spring.io/projects/spring-data-redis/&quot;&gt;https://spring.io/projects/spring-data-redis/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 예제에서는 Jedis를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성추가 (build.gradle)&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;&quot;org.springframework.boot:spring-boot-starter-data-redis&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 서버 설정 (appliction.properties)&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;### Redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=1234
spring.redis.pool.max-idle=10
spring.redis.pool.min-idle=5
spring.redis.pool.max-active=10
spring.redis.pool.max-wait=-10&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Configuration
public class RedisConfig {
    @Value(&quot;${spring.redis.host}&quot;)
    private String host;
    @Value(&quot;${spring.redis.port}&quot;)
    private int port;
    @Value(&quot;${spring.redis.password}&quot;)
    private String password;
    @Bean
    public RedisConnectionFactory connectionFactory() {
        JedisConnectionFactory jcf = new JedisConnectionFactory();
        jcf.setHostName(host);
        jcf.setPort(port);
        jcf.setPassword(password);
        return jcf;
    }
    @Bean
    public RedisTemplate&amp;lt;?, ?&amp;gt; redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate&amp;lt;byte[], byte[]&amp;gt; redisTemplate = new RedisTemplate&amp;lt;&amp;gt;();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StringReedisSerializer를 넘겨줘서 Sring 타입의 Key Value 템플릿을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하고 Redis UI를 통해서 보면 SpringSecurity에서의 세션관리가 Redis를 통해 되고있다는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/188285076-74ea9e9f-1ef6-4084-9af0-7eb2cf258442.png&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>Redis</category>
      <category>Spring</category>
      <category>캐쉬</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/167</guid>
      <comments>https://katastrophe.tistory.com/167#entry167comment</comments>
      <pubDate>Sat, 17 Sep 2022 17:01:26 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Springboot 개발 환경 분리 및 Datasource 암호화 하기</title>
      <link>https://katastrophe.tistory.com/162</link>
      <description>&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;개발 환경 분리하기&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;다른사람과 같이 진행하는 토이프로젝트에서 테스트용 환경과 개발 환경의 DB를 다르게 설정해 놨기 때문에 환경의 분리가 필요했다. 이 때 환경을 application.proerties 나 .yml 을 이용하여 분리를 한다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 application.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657039335275&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/gubun
    username: sa
    password:
    driver-class-name: org.h2.Driver
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: true
logging.level:
  org.hibernate.SQL: debug
#  org.hibernate.type: trace&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;AWS RDS 를 개발 db로 사용하기로 해서 기존 H2 DB를 쓰던 환경을 테스트로 두고, 새로 추가한 Postgres DB를 개발 환경으로(test,dev) 분리하기로 하였다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(SpringBoot 2.4 버전 이후로 on-profile 을 이용해야 한다. 기존의 방식은 deprecated 됨)&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;application.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657039548403&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  profiles:
    active: dev

logging.level:
  org.hibernate.SQL: debug
#  org.hibernate.type: trace&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application-test.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657039580431&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 8080

spring:
  config:
    activate:
      on-profile: test
  datasource:
    url: jdbc:h2:tcp://localhost/~/gubun
    username: sa
    password:
    driver-class-name: org.h2.Driver
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: 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;application-dev.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657039645588&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 9090

spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: ##DB위치
    username: ##이름
    password: ##패스워드
    driver-class-name: org.postgresql.Driver
  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;DataSource 암호화 하기&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;그런데 여기서 한 가지 문제는 &lt;span style=&quot;color: #ee2323;&quot;&gt;이 프로젝트가 Git으로 관리&lt;/span&gt; 되고 있다는 것이다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;만약 저 DataSource 그대로 Git에 올려버리면 보안문제는 물론이고 AWS에서 막아버린다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;.yml 을 gitignore을 통해 안 올리는 방법도 있겠지만 여기선 &lt;u&gt;암호화&lt;/u&gt;해서 올리려고 한다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jasypt&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Jasypt는 민감정보를 암호화 해주는 모듈로, 여기선 민감정보를 암호화 한 뒤에 환경변수로 복호화키를 설정하여 접속할 수 있게 할 것이다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.devglan.com/online-tools/jasypt-online-encryption-decryption&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.devglan.com/online-tools/jasypt-online-encryption-decryption&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657039947060&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;Programming Blog Article Feeds as per your Interest | DevGlan&quot; data-og-description=&quot;Best programming article feeds as per your Interest on different technologies. Subscribe to any technology and explore the best articles from around the web.&quot; data-og-host=&quot;www.devglan.com&quot; data-og-source-url=&quot;https://www.devglan.com/online-tools/jasypt-online-encryption-decryption&quot; data-og-url=&quot;https://www.devglan.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ufTpS/hyOZME0oFa/9NpsgUt1KBjwMgVRnXg6Qk/img.png?width=300&amp;amp;height=82&amp;amp;face=0_0_300_82,https://scrap.kakaocdn.net/dn/bRMyT6/hyOZEGUhIH/VN19LwKxqtckk4IkqPoT0k/img.png?width=300&amp;amp;height=82&amp;amp;face=0_0_300_82&quot;&gt;&lt;a href=&quot;https://www.devglan.com/online-tools/jasypt-online-encryption-decryption&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.devglan.com/online-tools/jasypt-online-encryption-decryption&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ufTpS/hyOZME0oFa/9NpsgUt1KBjwMgVRnXg6Qk/img.png?width=300&amp;amp;height=82&amp;amp;face=0_0_300_82,https://scrap.kakaocdn.net/dn/bRMyT6/hyOZEGUhIH/VN19LwKxqtckk4IkqPoT0k/img.png?width=300&amp;amp;height=82&amp;amp;face=0_0_300_82');&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;Programming Blog Article Feeds as per your Interest | DevGlan&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Best programming article feeds as per your Interest on different technologies. Subscribe to any technology and explore the best articles from around the web.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.devglan.com&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P4bV0/btrGzvTeTs6/9AJCGxf4XaGi2lKWFzh0kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P4bV0/btrGzvTeTs6/9AJCGxf4XaGi2lKWFzh0kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P4bV0/btrGzvTeTs6/9AJCGxf4XaGi2lKWFzh0kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP4bV0%2FbtrGzvTeTs6%2F9AJCGxf4XaGi2lKWFzh0kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;455&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;key를 이용하여 복호화를 수행하려면 Two Way Encryption이라는 옵션을 선택해야 한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;단방향 암호화가 아니기 때문에 복호화 키를 이용하면 바로 원문이 나온다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;참고로, 저 EncryptedString은 Encrypt를 수행할 때 마다 매번 다른 EncryptedString이 나온다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;이제 Spring에서 이를 이용해보자.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;의존성추가하기 (ulisesbocchio-jasypt)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1657040252250&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {

	// 암호화 모듈
	implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.4'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고) 3.0.3 부터 방식이 달라져서 이전 버전을 이용하면 동작 되지 않을 수도 있음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ulisesbocchio/jasypt-spring-boot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/ulisesbocchio/jasypt-spring-boot &lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657040390549&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - ulisesbocchio/jasypt-spring-boot: Jasypt integration for Spring boot&quot; data-og-description=&quot;Jasypt integration for Spring boot. Contribute to ulisesbocchio/jasypt-spring-boot development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ulisesbocchio/jasypt-spring-boot&quot; data-og-url=&quot;https://github.com/ulisesbocchio/jasypt-spring-boot&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cfRSao/hyOZNDYUsG/8S4R87PcAN7XqxeCsxU6xk/img.png?width=1200&amp;amp;height=600&amp;amp;face=980_127_1043_196&quot;&gt;&lt;a href=&quot;https://github.com/ulisesbocchio/jasypt-spring-boot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ulisesbocchio/jasypt-spring-boot&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cfRSao/hyOZNDYUsG/8S4R87PcAN7XqxeCsxU6xk/img.png?width=1200&amp;amp;height=600&amp;amp;face=980_127_1043_196');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - ulisesbocchio/jasypt-spring-boot: Jasypt integration for Spring boot&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Jasypt integration for Spring boot. Contribute to ulisesbocchio/jasypt-spring-boot development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Configuration 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1657040457685&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 서버 정보 암호화 하기위한 ConfigClass
 * 아래의 URL에서 encrypt decrypt 가능
 * https://www.devglan.com/online-tools/jasypt-online-encryption-decryption
 *
 * application-dev.yml 안에 있는 암호화된 DB 정보를 decrypt 하기 위해선 환경변수에서 값을 직접 입력받아야 합니다.
 */
@Configuration
@Slf4j
public class JasyptConfig {

    @Bean(&quot;jasyptStringEncryptor&quot;)
    public StringEncryptor stringEncryptor() {

        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword(System.getenv(&quot;JASYPT_PASSWORD&quot;));
        config.setAlgorithm(&quot;PBEWithMD5AndDES&quot;);
        config.setKeyObtentionIterations(&quot;1000&quot;);
        config.setPoolSize(&quot;1&quot;);
        config.setProviderName(&quot;SunJCE&quot;);
        config.setSaltGeneratorClassName(&quot;org.jasypt.salt.RandomSaltGenerator&quot;);
        config.setIvGeneratorClassName(&quot;org.jasypt.iv.NoIvGenerator&quot;);
        config.setStringOutputType(&quot;base64&quot;);
        encryptor.setConfig(config);
        return encryptor;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;config.setPassword(&lt;span style=&quot;color: #ee2323;&quot;&gt;System.getenv(&quot;JASYPT_PASSWORD&quot;)&lt;/span&gt;); 이 부분이 환경변수로 부터 키 값을 받아와서 설정해 주는 부분이다. 환경변수를 시스템변수에 추가해서 사용할 수 있는데, 필자는 null 이 반환이 되어서 직접 &lt;u&gt;runConfigurations&lt;/u&gt;에 추가해서 진행 하였다.&lt;/b&gt;&lt;/blockquote&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;330&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BzeFD/btrGyNmy4Ua/ZdvPp0hk55eTkCKDcnEI80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BzeFD/btrGyNmy4Ua/ZdvPp0hk55eTkCKDcnEI80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BzeFD/btrGyNmy4Ua/ZdvPp0hk55eTkCKDcnEI80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBzeFD%2FbtrGyNmy4Ua%2FZdvPp0hk55eTkCKDcnEI80%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;330&quot; height=&quot;126&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;126&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;1033&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGoIyL/btrGzOdWUWV/UmSPiXQm7KnHDUI7WwSqJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGoIyL/btrGzOdWUWV/UmSPiXQm7KnHDUI7WwSqJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGoIyL/btrGzOdWUWV/UmSPiXQm7KnHDUI7WwSqJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGoIyL%2FbtrGzOdWUWV%2FUmSPiXQm7KnHDUI7WwSqJ0%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;831&quot; height=&quot;540&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;671&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;그리고 다시 돌아와서 application-dev.yml 부분의 DB정보를 암호화 하여 나온 값들로 채워주면 되는데 ENC()로 감싸주면 끝&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: ENC(Akn44dXsH2btWIRNfqmNJc60LKUOWP2c+PVQ15+xSoZf4ddzGl1JcI13hbWZ9LUVeXo6kwdlBIuw+YKPaSUkK8h0sQWonoADl1lGUFwdS4NAaeQugwBBNJIu6kwxMBJ1)
    username: ENC(EmRRq+Xam8r36WFSPZas2UjrcY/DfwSv)
    password: ENC(daX0fX/g9fgOzXIz8TroQj50f1XwHf4z6qHiPA0Vv3g=)
    driver-class-name: org.postgresql.Driver
  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqEbGO/btrGBiFncyb/1WJYa4CCFc5qEd7aQLqkA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqEbGO/btrGBiFncyb/1WJYa4CCFc5qEd7aQLqkA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqEbGO/btrGBiFncyb/1WJYa4CCFc5qEd7aQLqkA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqEbGO%2FbtrGBiFncyb%2F1WJYa4CCFc5qEd7aQLqkA0%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;1850&quot; height=&quot;524&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;524&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>Backend/Spring</category>
      <category>application.yml 암호화</category>
      <category>Datasource 암호화</category>
      <category>Spring profile 분리</category>
      <category>Spring 민감정보 암호화</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/162</guid>
      <comments>https://katastrophe.tistory.com/162#entry162comment</comments>
      <pubDate>Wed, 6 Jul 2022 02:12:42 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 스프링 RestTemplate</title>
      <link>https://katastrophe.tistory.com/161</link>
      <description>&lt;h1&gt;&lt;b&gt;RestTemplate&lt;/b&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestTemplate&lt;/b&gt; 은 스프링3.0 이상 부터 지원하는 HTTP 통신, 그 중&lt;b&gt; RestAPI 를 기준&lt;/b&gt;으로 요청을 쉽게 할 수 있게 도와주는 &lt;b&gt;동기방식의 Rest 클라이언트&lt;/b&gt;.&lt;br /&gt;Hateoas 의 하이퍼 미디어 링크를 삽입할 수 있게 도와주는 Traverson과 함께 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 5.0 부터는 비동기 방식도 지원하는 WebClient를 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/175786742-8a70837b-8fa3-4c03-b3b9-c6ef1913f18b.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(서비스 &amp;rArr; other 서비스) 로 필요한 데이터를 받아올 때 사용 하며 예를들어,&lt;br /&gt;받아온 JSON 타입의 데이터를 Jackson2 MessageConverter을 통해 객체에 바인딩을 하여 우리가 쓸 수 있는 Object 형태로 받아 온다.&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RestTemplate 동작원리&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/175786725-0766f308-2032-45c8-9326-85b59b0a602b.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestTemplate &amp;rArr; HttpMessageConverter &amp;rArr; RequestEntity &amp;rArr; HttpMessageConverter &amp;rArr; ResponseEntity &amp;rArr; Object&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestTemplate 생성자 (여러가지 messageConverter들이 등록 되어 있음)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public RestTemplate() {
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
.............
.............
.............

if (jackson2Present) {
            this.messageConverters.add(new MappingJackson2HttpMessageConverter());
        }
        else if (gsonPresent) {
            this.messageConverters.add(new GsonHttpMessageConverter());
        }
        else if (jsonbPresent) {
            this.messageConverters.add(new JsonbHttpMessageConverter());
        }
        else if (kotlinSerializationJsonPresent) {
            this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
        }

        if (jackson2SmilePresent) {
            this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
        }
        if (jackson2CborPresent) {
            this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
        }
.............
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;RestTemplate 메서드 (Callback 처리 되는 것을 볼 수 있음 )&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;..............
..............
@Override
    @Nullable
    public &amp;lt;T&amp;gt; T getForObject(String url, Class&amp;lt;T&amp;gt; responseType, Object... uriVariables) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor&amp;lt;T&amp;gt; responseExtractor =
                new HttpMessageConverterExtractor&amp;lt;&amp;gt;(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    @Nullable
    public &amp;lt;T&amp;gt; T getForObject(String url, Class&amp;lt;T&amp;gt; responseType, Map&amp;lt;String, ?&amp;gt; uriVariables) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor&amp;lt;T&amp;gt; responseExtractor =
                new HttpMessageConverterExtractor&amp;lt;&amp;gt;(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    @Nullable
    public &amp;lt;T&amp;gt; T getForObject(URI url, Class&amp;lt;T&amp;gt; responseType) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor&amp;lt;T&amp;gt; responseExtractor =
                new HttpMessageConverterExtractor&amp;lt;&amp;gt;(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
    }
..............
..............&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestTemplate 으로 요청을 보내면 필요한 처리들을 백그라운드에서 Callback 메서드들을 호출하여 처리 후 데이터를 받아오는 방식으로 동작.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RestTemplate으로 REST 엔드포인트 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestTemplate&lt;/b&gt; 클라이언트는 다른 API에 요청을 하기 위한 12개의 메서드들이 있다.&lt;br /&gt;파라미터들이 다른 오버로딩 된 메서드들을 합치면 총 41개의 메서드들이 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;HTTP Method&lt;/th&gt;
&lt;th&gt;ResponseType&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;delete(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Delete 요청 수행&lt;/td&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exchange(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP 요청을 수행한 후 ResponseEntity를 반환&lt;/td&gt;
&lt;td&gt;ALL&lt;/td&gt;
&lt;td&gt;ResponseEntity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;excute(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP 요청을 수행한 후 Object를 반환&lt;/td&gt;
&lt;td&gt;ALL&lt;/td&gt;
&lt;td&gt;Object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;getFor[Entity/Object] (&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Get 요청을 수행한 후 [ResponseEntity / Object] 를 반환&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;ResponseEntity / T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;headForHeaders(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Head 요청을 전송하며 헤더 정보를 반환&lt;/td&gt;
&lt;td&gt;HEAD&lt;/td&gt;
&lt;td&gt;HttpHeaders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;optionsForAllow(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Option 요청을 전송하며 Allow 헤더를 반환 ( HTTP 헤더 정보)&lt;/td&gt;
&lt;td&gt;OPTIONS&lt;/td&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;patchForObject(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Patch 요청을 전송하며 결과 객체를 반환&lt;/td&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;postFor[Entity/Object] (&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Post 를 요청을 수행하며 [ResponseEntity / Object] 를 반환&lt;/td&gt;
&lt;td&gt;Post&lt;/td&gt;
&lt;td&gt;ResponseEntity / T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostForLocation(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Post 를 수행한 후 생성되는 URI을 반환&lt;/td&gt;
&lt;td&gt;Post&lt;/td&gt;
&lt;td&gt;URI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Put(&amp;hellip;)&lt;/td&gt;
&lt;td&gt;HTTP Put 을 수행&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본적으로 HTTP Method 에 맞는 method 들이 하나씩 있으며 , exchange 와 excute 는 더 넓은 범위의 모든 요청을 수행할 수 있는 low 레벨 메서드이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;RestTemplate Bean 을 주입받아 사용할 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;GET 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
@Slf4j
@RequiredArgsConstructor
public class TacoCloudClient {

    private final RestTemplate rest;
    private final Traverson traverson;

    //
    // GET examples
    //

    public Ingredient getIngredientById(String ingredientId) {
        return rest.getForObject(&quot;http://localhost:8080/ingredients/{id}&quot;,
                Ingredient.class, ingredientId);
    }

    // 모든 요청을 수행할 수 있는 메서드 이므로 요청 method 도 같이 파라미터에 지정
    public List&amp;lt;Ingredient&amp;gt; getAllIngredients() {
        return rest.exchange(&quot;http://localhost:8080/ingredients&quot;,
                        HttpMethod.GET, null, new ParameterizedTypeReference&amp;lt;List&amp;lt;Ingredient&amp;gt;&amp;gt;() {})
                .getBody();
    }

    // Map 으로 키 지정
    public Ingredient getIngredientById(String ingredientId) {
        Map&amp;lt;String, String&amp;gt; urlVariables = new HashMap&amp;lt;&amp;gt;();
        urlVariables.put(&quot;id&quot;, ingredientId);
        return rest.getForObject(&quot;http://localhost:8080/ingredients/{id}&quot;,
                Ingredient.class, urlVariables);
    }

    // URI 객체를 생성하여 전송
    public Ingredient getIngredientById(String ingredientId) {
        Map&amp;lt;String, String&amp;gt; urlVariables = new HashMap&amp;lt;&amp;gt;();
        urlVariables.put(&quot;id&quot;, ingredientId);
        URI url = UriComponentsBuilder
                .fromHttpUrl(&quot;http://localhost:8080/ingredients/{id}&quot;)
                .build(urlVariables);
        return rest.getForObject(url, Ingredient.class);
    }

    // header 와 body 정보가 필요할 때 요청 후 ResponseEntity 로 반환
    public Ingredient getIngredientById(String ingredientId) {
        ResponseEntity&amp;lt;Ingredient&amp;gt; responseEntity =
                rest.getForEntity(&quot;http://localhost:8080/ingredients/{id}&quot;,
                        Ingredient.class, ingredientId);
        log.info(&quot;Fetched time: &quot; + responseEntity.getHeaders().getDate());
        return responseEntity.getBody();
    }
}&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;Post 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
@Slf4j
@RequiredArgsConstructor
public class TacoCloudClient {

    private final RestTemplate rest;
    private final Traverson traverson;

    //
    // POST examples
    //
    public Ingredient createIngredient(Ingredient ingredient) {
        return rest.postForObject(&quot;http://localhost:8080/ingredients&quot;,
                ingredient, Ingredient.class);
    }

    // 생성된 URI 반환
    public URI createIngredient(Ingredient ingredient) {
        return rest.postForLocation(&quot;http://localhost:8080/ingredients&quot;,
                ingredient, Ingredient.class);
    }

    public Ingredient createIngredient(Ingredient ingredient) {
        ResponseEntity&amp;lt;Ingredient&amp;gt; responseEntity =
                rest.postForEntity(&quot;http://localhost:8080/ingredients&quot;,
                        ingredient,
                        Ingredient.class);
        log.info(&quot;New resource created at &quot; + responseEntity.getHeaders().getLocation());
        return responseEntity.getBody();
    }
}&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;Put 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Service
@Slf4j
@RequiredArgsConstructor
public class TacoCloudClient {

  private final RestTemplate rest;
  private final Traverson traverson;

  //
  // PUT examples
  //
  public void updateIngredient(Ingredient ingredient) {
    rest.put(&quot;http://localhost:8080/ingredients/{id}&quot;,
          ingredient, ingredient.getId());
  }
}&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;Delete 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Service
@Slf4j
@RequiredArgsConstructor
public class TacoCloudClient {

  private final RestTemplate rest;
  private final Traverson traverson;

  //
  // DELETE examples
  //

  public void deleteIngredient(Ingredient ingredient) {
    rest.delete(&quot;http://localhost:8080/ingredients/{id}&quot;,
        ingredient.getId());
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;RestTemplate의 한계?&lt;/b&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링 5.0 부터는 WebClient&lt;/b&gt;를 지원하고, 이를 사용하도록 권고하고 있다. 그 동안 비동기 방식은 AsyncRestTemplate이 지원해 주었는데 deprecated되 었다.&lt;br /&gt;이어서 RestTemplate을 deprecated 될 가능성이 있다고 한다. 그 이유는..?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고) &lt;a href=&quot;https://velog.io/@nittre/%EB%B8%94%EB%A1%9C%ED%82%B9-Vs.-%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EB%8F%99%EA%B8%B0-Vs.-%EB%B9%84%EB%8F%99%EA%B8%B0&quot;&gt;동기 vs 비동기 , blocking vs non-blocking&lt;/a&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RestTemplate&lt;/th&gt;
&lt;th&gt;WebClient&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;td&gt;Non-Blocking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;Async&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MultiThread&lt;/td&gt;
&lt;td&gt;SingleThread&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestTemplate 과 WebClient의 Blocking 이나 Non-Blocking 이냐 에 따라 성능차이가 동시 사용자가 많아 질수록 급격하게 차이가 난다고 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/175786731-f7ca0e64-3287-4bcb-8188-99d7b56ab591.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boot 1은 &lt;b&gt;WebClient&lt;/b&gt; , Boot 2 는 SpringMVC의 &lt;b&gt;RestTemplate&lt;/b&gt; 이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 (SpringMVC) &lt;b&gt;Sync-Blocking&lt;/b&gt; 방식은 쓰레드 풀이 수용할 수 있는 요청만 동시적으로 처리하고 그 이상은 큐잉되어 대기하게 되는데 &lt;b&gt;이 max-size 를 지속적으로 초과하게 된다면&lt;/b&gt; 전체 대기시간이 점점 늘어나게 된다. ( Thread poll hell 현상)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 I/O 작업에 있어서 효율을 낼 수 있는&lt;b&gt; async &amp;amp; non-blocking&lt;/b&gt; 과&lt;b&gt; event-driven&lt;/b&gt; 방식을 통해 처리할 수 있는 Webclient를 쓰도록 권장하는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 ) &lt;a href=&quot;https://alwayspr.tistory.com/44&quot;&gt;Spring Webflux 는 어떻게 적은 리소스로 많은 트래픽을 감당할까?&lt;/a&gt;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>restapi</category>
      <category>RestTemplate</category>
      <category>Spring RestTemplate</category>
      <category>스프링 인 액션</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/161</guid>
      <comments>https://katastrophe.tistory.com/161#entry161comment</comments>
      <pubDate>Sun, 26 Jun 2022 04:06:53 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] RestAPI와 Hateoas로 링크 삽입하기</title>
      <link>https://katastrophe.tistory.com/160</link>
      <description>&lt;h1&gt;&lt;b&gt;Hateoas&lt;/b&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 API 응답 방법에서 &lt;b&gt;만약 특정 URL에 대한 요청을 했는데, 해당 URL이 바뀌었다면&lt;/b&gt; 404 NotFound를 반환하기 때문에 이를 &lt;span style=&quot;color: #ee2323;&quot;&gt;동적으로 Hypermedia 링크를 삽입된 응답을 반환&lt;/span&gt;하기 위한 메커니즘이 Hateoas 이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hateoas&lt;/b&gt;(Hypermedia As The Engine Of Application State)는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Hypermedia를 Application의 상태를 관리하기 위해 도입&lt;/span&gt;된 개념&lt;/b&gt;. 즉 , 클라이언트는 서버와 동적으로 상호작용이 가능하도록 해야함. &amp;rArr; &lt;b&gt;요청에 필요한 URI를 응답에 포함시켜 반환&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RestAPI 구현레벨&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/174455306-7d6087d3-5a95-4fb9-ad90-28d2e3dc7b82.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RestAPI에도 구현 레벨이 있는데 &lt;b&gt;Hateoas 는 마지막 Level3&lt;/b&gt; 의 단계이다. [&lt;a href=&quot;https://velog.io/@younge/REST-API-%EC%84%B1%EC%88%99%EB%8F%84-%EB%AA%A8%EB%8D%B8-Maturity-Model-eqqyjqff&quot;&gt;참고링크&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;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-starter-hateoas'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고)&lt;/b&gt; &lt;b&gt;스프링부트 2.2 이상 부터는 기존 Resource 에서 EntityModel로 바뀌었음&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이전버전&lt;/th&gt;
&lt;th&gt;2.2이후&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ResourceSupport&lt;/td&gt;
&lt;td&gt;RepresentationalModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource&lt;/td&gt;
&lt;td&gt;EntityModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource&lt;/td&gt;
&lt;td&gt;CollectionModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PagedResources&lt;/td&gt;
&lt;td&gt;PagedModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ResourceAssembler&lt;/td&gt;
&lt;td&gt;RepresentationalModelAssembler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ControllerLinkBuilder&lt;/td&gt;
&lt;td&gt;WebMvcLinkBuilder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;리소스에 하이퍼링크 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환타입이 List 타입이므로 CollectionModel.of() 메서드를 이용해서 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;linkTo&lt;/b&gt; 메서드 안에 특정 Controller를 지정함으로써 해당 Controller의 요청에 대한 삽입할 Link를 생성할 수 있다. &amp;rArr; &lt;u&gt;URI 가 변경되어도 변경된 URI에 대한 링크가 반환이 된다.&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linkTo 안에 &lt;b&gt;methodOn&lt;/b&gt;을 이용해서 특정 메서드를 지정할 수도 있음&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;DesignTacoApiController.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@RestController
@RequestMapping(path = &quot;/api/design&quot;,produces = &quot;application/json&quot;)
@RequiredArgsConstructor
@CrossOrigin(origins = &quot;*&quot;)
public class DesignTacoApiController {

    private final TacoRepository tacoRepository;

    @GetMapping(&quot;/recent&quot;)
    public CollectionModel&amp;lt;Taco&amp;gt; recentTacos(){
        PageRequest pageRequest = PageRequest.of(0, 12, Sort.by(&quot;createdAt&quot;).descending());

        List&amp;lt;Taco&amp;gt; tacos = tacoRepository.findAll(pageRequest).getContent();
        CollectionModel&amp;lt;Taco&amp;gt; recentResources = CollectionModel.of(tacos);

        Link link = linkTo(methodOn(DesignTacoApiController.class).recentTacos())
                .withRel(&quot;recents&quot;);
        recentResources.add(link);

        return recentResources;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;_embedded&quot;: {
        &quot;tacoList&quot;: [
            {
                &quot;id&quot;: 2,
                &quot;createdAt&quot;: &quot;2022-06-18T19:07:13.816+00:00&quot;,
                &quot;name&quot;: &quot;taco123&quot;,
                &quot;ingredients&quot;: [
                    {
                        &quot;id&quot;: &quot;FLTO&quot;,
                        &quot;name&quot;: &quot;Flour Tortilla&quot;,
                        &quot;type&quot;: &quot;WRAP&quot;
                    },
                    {
                        &quot;id&quot;: &quot;GRBF&quot;,
                        &quot;name&quot;: &quot;Ground Beef&quot;,
                        &quot;type&quot;: &quot;PROTEIN&quot;
                    },
                    {
                        &quot;id&quot;: &quot;JACK&quot;,
                        &quot;name&quot;: &quot;Monterrey Jack&quot;,
                        &quot;type&quot;: &quot;CHEESE&quot;
                    },
                    {
                        &quot;id&quot;: &quot;LETC&quot;,
                        &quot;name&quot;: &quot;Lettuce&quot;,
                        &quot;type&quot;: &quot;VEGGIES&quot;
                    },
                    {
                        &quot;id&quot;: &quot;SLSA&quot;,
                        &quot;name&quot;: &quot;Salsa&quot;,
                        &quot;type&quot;: &quot;SAUCE&quot;
                    }
                ]
            }
        ]
    },
    &quot;_links&quot;: {
        &quot;recents&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/api/design/recent&quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;리소스 어셈블러&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;taco가 만약 여러개라면 반복문을 통해 여러개의 Link 객체를 만들어 삽입해야 하므로 &lt;span style=&quot;color: #ee2323;&quot;&gt;RepresentationalModelAssembler를 구현한 Assembler&lt;/span&gt;를 만들어서 해결한다.&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;게다가 TacoList를 보면 id값이 노출되어 있는데, 클라이언트 입장에서는 id값을 알 필요가 없기 때문에 별도의 RepresentationalModel을 상속받은 유틸리티 class를 생성하여 _self 링크가 이를 대체하게 한다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;TacoResources.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Relation(value = &quot;taco&quot;,collectionRelation = &quot;tacos&quot;)
public class TacoResources extends RepresentationModel&amp;lt;TacoResources&amp;gt; {

    @Getter
    private final String name;

    @Getter
    private final Date createAt;

    @Getter
    private final List&amp;lt;Ingredients&amp;gt; ingredients;

    public TacoResources(Taco taco) {
        this.name=taco.getName();
        this.createAt=taco.getCreatedAt();
        this.ingredients=taco.getIngredients();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, &lt;b&gt;@Relation&lt;/b&gt; 으로 이름을 지정해주면 json안에 임의로 생성된 tacoList 변수명을 다른 변수명으로 지정해 줄 수 있음&lt;/p&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;어셈블러 TacoResourceAssembler.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Component
public class TacoResourceAssembler implements RepresentationModelAssembler&amp;lt;Taco, EntityModel&amp;lt;TacoResources&amp;gt;&amp;gt; {

    @Override
    public EntityModel&amp;lt;TacoResources&amp;gt; toModel(Taco taco) {

        return EntityModel.of(new TacoResources(taco),
                linkTo(methodOn(DesignTacoApiController.class).findTacoById(taco.getId())).withSelfRel()
        );
    }

    @Override
    public CollectionModel&amp;lt;EntityModel&amp;lt;TacoResources&amp;gt;&amp;gt; toCollectionModel(Iterable&amp;lt;? extends Taco&amp;gt; tacos) {
        return RepresentationModelAssembler.super.toCollectionModel(tacos).add(
                        linkTo(DesignTacoApiController.class).withSelfRel(),
                linkTo(methodOn(DesignTacoApiController.class).recentTacos()).withRel(&quot;recents&quot;)
        );
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단일 객체&lt;/b&gt;를 반환하는 toModel 에서는&lt;span style=&quot;color: #ee2323;&quot;&gt; taco &amp;rArr; tacoResources&lt;/span&gt;로 변환하고&lt;b&gt; EntityModel에 링크를 삽입하여 반환&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;을 반환하는 toCollectionModel 에서는 &lt;span style=&quot;color: #ee2323;&quot;&gt;tacos &amp;rArr; CollectionModel&lt;/span&gt;로 변환하는데, &lt;b&gt;컬렉션으로 반환될 수 있는 모든 경우를 지정하여 각각에 맞는 Link가 삽입&lt;/b&gt;될 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨트롤러 DesignTacoApiController.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(path = &quot;/api/design&quot;,produces = &quot;application/json&quot;)
@RequiredArgsConstructor
@CrossOrigin(origins = &quot;*&quot;)
public class DesignTacoApiController {

    private final TacoRepository tacoRepository;
    private final TacoResourceAssembler assembler;

    @GetMapping(&quot;/recent&quot;)
    public CollectionModel&amp;lt;EntityModel&amp;lt;TacoResources&amp;gt;&amp;gt; recentTacos(){
        PageRequest pageRequest = PageRequest.of(0, 12, Sort.by(&quot;createdAt&quot;).descending());

        List&amp;lt;Taco&amp;gt; tacos = tacoRepository.findAll(pageRequest).getContent();

        return assembler.toCollectionModel(tacos);
    }

    @GetMapping
    public List&amp;lt;Taco&amp;gt; findAll(){
        return tacoRepository.findAll();
    }

    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&amp;lt;Taco&amp;gt; findTacoById(@PathVariable Long id){
        Optional&amp;lt;Taco&amp;gt; findTaco = tacoRepository.findById(id);

        return findTaco.map(taco -&amp;gt; new ResponseEntity&amp;lt;&amp;gt;(taco, HttpStatus.OK))
                .orElseGet(() -&amp;gt; new ResponseEntity&amp;lt;&amp;gt;(null, HttpStatus.NOT_FOUND));
    }
}&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 class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;_embedded&quot;: {
        &quot;tacos&quot;: [
            {
                &quot;name&quot;: &quot;taco1&quot;,
                &quot;createAt&quot;: &quot;2022-06-18T19:31:40.148+00:00&quot;,
                &quot;ingredients&quot;: [
                    {
                        &quot;id&quot;: &quot;FLTO&quot;,
                        &quot;name&quot;: &quot;Flour Tortilla&quot;,
                        &quot;type&quot;: &quot;WRAP&quot;
                    },
                    {
                        &quot;id&quot;: &quot;GRBF&quot;,
                        &quot;name&quot;: &quot;Ground Beef&quot;,
                        &quot;type&quot;: &quot;PROTEIN&quot;
                    },
                    {
                        &quot;id&quot;: &quot;CHED&quot;,
                        &quot;name&quot;: &quot;Cheddar&quot;,
                        &quot;type&quot;: &quot;CHEESE&quot;
                    },
                    {
                        &quot;id&quot;: &quot;TMTO&quot;,
                        &quot;name&quot;: &quot;Diced Tomatoes&quot;,
                        &quot;type&quot;: &quot;VEGGIES&quot;
                    },
                    {
                        &quot;id&quot;: &quot;SLSA&quot;,
                        &quot;name&quot;: &quot;Salsa&quot;,
                        &quot;type&quot;: &quot;SAUCE&quot;
                    }
                ],
                &quot;_links&quot;: {
                    &quot;self&quot;: {
                        &quot;href&quot;: &quot;http://localhost:8080/api/design/2&quot;
                    }
                }
            }
        ]
    },
    &quot;_links&quot;: {
        &quot;self&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/api/design&quot;
        },
        &quot;recents&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/api/design/recent&quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;객체안의 객체에 링크를 삽입하기&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Taco 안에 Ingredients 객체에 대한 링크도 나타낼 수 있는데 기존 &lt;b&gt;TacoResources라는 유틸리티 클래스에 Ingrerdients의 어셈블러를 넣어줘서&lt;/b&gt; 추가적인 링크를 삽입할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Getter
@Setter
@Relation(value = &quot;ingredient&quot;,collectionRelation = &quot;ingredients&quot;)
public class IngredientResources extends RepresentationModel&amp;lt;IngredientResources&amp;gt; {

    private String name;
    private Type type;

    public IngredientResources(Ingredient ingredient) {
        this.name = ingredient.getName();
        this.type = ingredient.getType();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Relation(value = &quot;taco&quot;,collectionRelation = &quot;tacos&quot;)
public class TacoResources extends RepresentationModel&amp;lt;TacoResources&amp;gt; {

    private static final IngredientResourceAssembler ingredientResourceAssembler = new IngredientResourceAssembler();

    @Getter
    private final String name;

    @Getter
    private final Date createAt;

    @Getter
    private final CollectionModel&amp;lt;EntityModel&amp;lt;IngredientResources&amp;gt;&amp;gt; ingredients;

    public TacoResources(Taco taco) {
        this.name=taco.getName();
        this.createAt=taco.getCreatedAt();
        this.ingredients=ingredientResourceAssembler.toCollectionModel(taco.getIngredients());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;List 타입의 컬렉션 필드 대신에 CollectionModel을 필드로 대체한다.&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;컨트롤러 IngredientController.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(path=&quot;/ingredients&quot;, produces=&quot;application/json&quot;)
@CrossOrigin(origins=&quot;*&quot;)
@RequiredArgsConstructor
public class IngredientController {

    private IngredientRepository ingredientRepository;

    @GetMapping
    public Iterable&amp;lt;Ingredient&amp;gt; findAllIngredients() {
        return ingredientRepository.findAll();
    }

    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&amp;lt;Ingredient&amp;gt; findIngredientById(@PathVariable String id){
        Optional&amp;lt;Ingredient&amp;gt; findIngredient = ingredientRepository.findById(id);

        return findIngredient.map(ingredient -&amp;gt; new ResponseEntity&amp;lt;&amp;gt;(ingredient, HttpStatus.OK))
                .orElseGet(() -&amp;gt; new ResponseEntity&amp;lt;&amp;gt;(null, HttpStatus.NOT_FOUND));
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;어셈블러 IngredientResourceAssembler.class&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class IngredientResourceAssembler implements RepresentationModelAssembler&amp;lt;Ingredient, EntityModel&amp;lt;IngredientResources&amp;gt;&amp;gt; {

    @Override
    public EntityModel&amp;lt;IngredientResources&amp;gt; toModel(Ingredient ingredient) {
        return EntityModel.of(new IngredientResources(ingredient),
                linkTo(methodOn(IngredientController.class).findIngredientById(ingredient.getId())).withSelfRel()
        );
    }

    @Override
    public CollectionModel&amp;lt;EntityModel&amp;lt;IngredientResources&amp;gt;&amp;gt; toCollectionModel(Iterable&amp;lt;? extends Ingredient&amp;gt; ingredients) {

        return RepresentationModelAssembler.super.toCollectionModel(ingredients).add(
                linkTo(IngredientController.class).withSelfRel()
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;_embedded&quot;: {
        &quot;tacos&quot;: [
            {
                &quot;name&quot;: &quot;taco2&quot;,
                &quot;createAt&quot;: &quot;2022-06-18T19:49:45.909+00:00&quot;,
                &quot;ingredients&quot;: {
                    &quot;_embedded&quot;: {
                        &quot;ingredients&quot;: [
                            {
                                &quot;name&quot;: &quot;Corn Tortilla&quot;,
                                &quot;type&quot;: &quot;WRAP&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/COTO&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Ground Beef&quot;,
                                &quot;type&quot;: &quot;PROTEIN&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/GRBF&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Monterrey Jack&quot;,
                                &quot;type&quot;: &quot;CHEESE&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/JACK&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Diced Tomatoes&quot;,
                                &quot;type&quot;: &quot;VEGGIES&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/TMTO&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Lettuce&quot;,
                                &quot;type&quot;: &quot;VEGGIES&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/LETC&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Salsa&quot;,
                                &quot;type&quot;: &quot;SAUCE&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/SLSA&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Sour Cream&quot;,
                                &quot;type&quot;: &quot;SAUCE&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/SRCR&quot;
                                    }
                                }
                            }
                        ]
                    },
                    &quot;_links&quot;: {
                        &quot;self&quot;: {
                            &quot;href&quot;: &quot;http://localhost:8080/ingredients&quot;
                        }
                    }
                },
                &quot;_links&quot;: {
                    &quot;self&quot;: {
                        &quot;href&quot;: &quot;http://localhost:8080/api/design/4&quot;
                    }
                }
            },
            {
                &quot;name&quot;: &quot;taco1&quot;,
                &quot;createAt&quot;: &quot;2022-06-18T19:49:33.849+00:00&quot;,
                &quot;ingredients&quot;: {
                    &quot;_embedded&quot;: {
                        &quot;ingredients&quot;: [
                            {
                                &quot;name&quot;: &quot;Flour Tortilla&quot;,
                                &quot;type&quot;: &quot;WRAP&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/FLTO&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Ground Beef&quot;,
                                &quot;type&quot;: &quot;PROTEIN&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/GRBF&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Carnitas&quot;,
                                &quot;type&quot;: &quot;PROTEIN&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/CARN&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Cheddar&quot;,
                                &quot;type&quot;: &quot;CHEESE&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/CHED&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Diced Tomatoes&quot;,
                                &quot;type&quot;: &quot;VEGGIES&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/TMTO&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Lettuce&quot;,
                                &quot;type&quot;: &quot;VEGGIES&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/LETC&quot;
                                    }
                                }
                            },
                            {
                                &quot;name&quot;: &quot;Salsa&quot;,
                                &quot;type&quot;: &quot;SAUCE&quot;,
                                &quot;_links&quot;: {
                                    &quot;self&quot;: {
                                        &quot;href&quot;: &quot;http://localhost:8080/ingredients/SLSA&quot;
                                    }
                                }
                            }
                        ]
                    },
                    &quot;_links&quot;: {
                        &quot;self&quot;: {
                            &quot;href&quot;: &quot;http://localhost:8080/ingredients&quot;
                        }
                    }
                },
                &quot;_links&quot;: {
                    &quot;self&quot;: {
                        &quot;href&quot;: &quot;http://localhost:8080/api/design/2&quot;
                    }
                }
            }
        ]
    },
    &quot;_links&quot;: {
        &quot;self&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/api/design&quot;
        },
        &quot;recents&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/api/design/recent&quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고) [&lt;a href=&quot;https://docs.spring.io/spring-hateoas/docs/current/reference/html/&quot;&gt;스프링 Hateoas 공식문서&lt;/a&gt;]&lt;br /&gt;출처: 스프링 인 액션&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>restapi</category>
      <category>spring hateoas</category>
      <category>스프링 인 액션</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/160</guid>
      <comments>https://katastrophe.tistory.com/160#entry160comment</comments>
      <pubDate>Sun, 19 Jun 2022 05:05:29 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Http 통신과 관련된 Annotations 간단 정리</title>
      <link>https://katastrophe.tistory.com/159</link>
      <description>&lt;h1&gt;&lt;strong&gt;HTTP 통신과 관련된 Annotation들&lt;/strong&gt;&lt;/h1&gt;
&lt;h2&gt;&lt;strong&gt;@RequestMapping (요청을 받을 때)&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;특정 URL에 요청을 보내면 Controller에서 이를 처리하는데, URL과 HTTP Method에 따라 맞는 요청을 받아 처리한다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;@Controller
@Slf4j
@SessionAttributes(&amp;quot;order&amp;quot;)
@RequiredArgsConstructor
@RequestMapping(&amp;quot;/orders&amp;quot;)
public class OrderController {

    @GetMapping(&amp;quot;/current&amp;quot;) // @RequestMapping(value = &amp;quot;/current&amp;quot;,method = RequestMethod.GET)
    public String orderForm(@AuthenticationPrincipal User user,Order order){
    ...

    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Class 레벨에 &lt;code&gt;@RequestMapping&lt;/code&gt;을 걸어두고 Method레벨에 &lt;code&gt;@GetMapping&lt;/code&gt; 같이 특정 HTTP Method를 알 수 있는 애노테이션을 붙이는 방식이 선호되는 방식&lt;/strong&gt;  &lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;HTTP Method 종류와 애노테이션&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;&lt;code&gt;@GetMapping&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;HTTP &lt;code&gt;Get&lt;/code&gt; 방식의 Method를 처리&lt;/li&gt;
&lt;li&gt;Get Method 요청을 처리하는데 추가로 전달할 요청 관련 정보는 쿼리파라미터로 같이 전달&lt;/li&gt;
&lt;li&gt;HTTP Form 에 입력한 값은 쿼리 파라미터로 전달해서 view를 리턴하거나 정보를 리턴함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@RequestParam&lt;/code&gt; 이나 &lt;code&gt;@ModelAttribute&lt;/code&gt;를 통해 전달 받은 요청 파라미터를 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// RequestParam : String, Integer 과 같은 primitive 타입
    @GetMapping
    public String ordersForUser(@AuthenticationPrincipal User user,Model model
    , @RequestParam int limit,@RequestParam int offset){

      PageRequest pageable = PageRequest.of(offset,limit);
      model.addAttribute(&amp;quot;orders&amp;quot;,orderRepository.findByUserOrderByPlacedAtDesc(user,pageable));

      return &amp;quot;orderList&amp;quot;;
    }&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// ModelAttribute : object 타입

    @GetMapping(&amp;quot;/current&amp;quot;)
    public String orderForm(@AuthenticationPrincipal User user,@ModelAttribute Order order){
        if (order.getDeliveryName() == null) {
            order.setDeliveryName(user.getFullname());
        }
        if (order.getDeliveryStreet() == null) {
            order.setDeliveryStreet(user.getStreet());
        }
        if (order.getDeliveryCity() == null) {
            order.setDeliveryCity(user.getCity());
        }
        if (order.getDeliveryState() == null) {
            order.setDeliveryState(user.getState());
        }
        if (order.getDeliveryZip() == null) {
            order.setDeliveryZip(user.getZip());
        }
        return &amp;quot;orderForm&amp;quot;;
    }&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;&lt;code&gt;@PostMapping&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;HTTP &lt;code&gt;Post&lt;/code&gt; 방식의 Method를 처리&lt;/li&gt;
&lt;li&gt;요청을 처리하는데 추가로 전달할 요청 관련 정보는 HTTP body에도 담아 전달&lt;/li&gt;
&lt;li&gt;HTTP Form 에 입력한 값은 Get방식과 마찬가지로 쿼리스트링 방식으로 전달해서 로직을 수행&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@RequestBody&lt;/code&gt; 나 &lt;code&gt;@ModelAttribute&lt;/code&gt;를 통해 전달 받은 요청 파라미터를 처리&lt;/li&gt;
&lt;li&gt;전달 받은 데이터는 MessageConverter와 자바 reflection을 통해 객체 바인딩이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    @PostMapping // ModelAttribute 사용 (파라미터 형태로 보내질 때)
    public String processOrder(@Valid Order order, Errors errors, SessionStatus sessionStatus, @AuthenticationPrincipal User user){

        if(errors.hasErrors()){
            return &amp;quot;orderForm&amp;quot;;
        }

        log.info(&amp;quot;order submitted : {}&amp;quot;,order);
        order.setUser(user);
        orderRepository.save(order);
        sessionStatus.setComplete();
        return &amp;quot;redirect:/&amp;quot;;
    }&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;    @PostMapping // RequestBody 사용 (Body에 데이터를 담아 보내질 때)
    public String processOrder(@Valid @RequestBody Order order, Errors errors, SessionStatus sessionStatus, @AuthenticationPrincipal User user){

        if(errors.hasErrors()){
            return &amp;quot;orderForm&amp;quot;;
        }
        order.setUser(user);
        orderRepository.save(order);
        sessionStatus.setComplete();
        return &amp;quot;redirect:/&amp;quot;;
    }&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;&lt;code&gt;@PutMapping&lt;/code&gt;, &lt;code&gt;@PatchMapping&lt;/code&gt;, &lt;code&gt;@DeleteMapping&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Html Form&lt;/code&gt;은 &lt;code&gt;Get&lt;/code&gt;과 &lt;code&gt;Post&lt;/code&gt;만 동작하지만, PostMapping 처럼 동작하는 HTTP Method들이 있음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PutMapping&lt;/code&gt; 과 &lt;code&gt;PatchMapping&lt;/code&gt;은 수정관련 요청 Method인데 차이점은 부분수정vs전체수정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DeleteMapping&lt;/code&gt;은 말 그대로 데이터를 삭제할 때 요청하는 Method  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;@RequestParam&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;요청 파라미터를 받는 방식 중 하나로 primitive 타입만 받는다.&lt;/li&gt;
&lt;li&gt;보낼때 파마미터 변수와 메소드의 파라미터 변수와 같으면 생략이 가능함  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;@ResponseBody&lt;/code&gt; (데이터를 보낼 때)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;ViewResolver를 통해 서버에서 view 를 렌더링해서 보여주는 응답을 할 수 있지만&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;API서버로서 데이터를 보낼 때는 &lt;code&gt;String&lt;/code&gt; 형식이나 &lt;code&gt;Json&lt;/code&gt; 형식으로 body데이터를 보낼 수 있음&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    @GetMapping(&amp;quot;/list&amp;quot;)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ArrayList&amp;lt;List&amp;lt;Taco&amp;gt;&amp;gt; getTacos(@AuthenticationPrincipal User user){

        Pageable pageable = PageRequest.of(0, orderProps.getPageSize());
        List&amp;lt;Order&amp;gt; orders = orderRepository.findByUserOrderByPlacedAtDesc(user, pageable);

        return orders.stream()
                .map(Order::getTacos)
                .collect(Collectors.toCollection(ArrayList::new));
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;헤더 설정이 필요한 경우 &lt;code&gt;HttpEnity&lt;/code&gt; 형태로 return 도 가능함&lt;/strong&gt;  &lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;&lt;code&gt;@PathVariable&lt;/code&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;경로 변수라고도 하며 URL에 쿼리스트링 형식으로 데이터를 보내는 것과 다르게 URL 경로중 일부로 작용함.&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;ex) &lt;a href=&quot;http://www.taco.com/&quot;&gt;www.tacocloud.com/&lt;/a&gt;taco/1&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;    private final TacoRepository tacoRepository;
    @GetMapping(&amp;quot;/{tacoNum}&amp;quot;)
    @ResponseBody
    public Taco getTaco(@PathVariable Long tacoNum){

        return tacoRepository.findById(tacoNum).get();
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;위와 같이 쿼리 스트링 형식이 아닌 경로에 포함된 변수로서 사용이 가능&lt;/strong&gt;  &lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;&lt;code&gt;RedirectAttributes&lt;/code&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;요청한 데이터와 관련되거나 포함된 URL을 다시 redirect해서 보여줄때 사용한다. 예를들어 PRG (post redirect get ) 방식으로 다시 Get을 요청 하게 만들 때 사용함&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;    private final TacoRepository tacoRepository;

    @PostMapping
    public String processDesign(@Valid Taco design, Errors errors,RedirectAttributes redirectAttributes) {
        if (errors.hasErrors()) {
            return &amp;quot;design&amp;quot;;
        }

        Taco savedTaco = tacoRepository.save(design);
        redirectAttributes.addAttribute(&amp;quot;tacoNum&amp;quot;,savedTaco.getId());

        return &amp;quot;redirect:/taco/{tacoNum}&amp;quot;;
    }&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;&lt;code&gt;@ModelAttribute&lt;/code&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ModeAttribute는 object 와 String 을 서로 변환, 역변환을 도와주는 타입 컨버터가 적용되어 있다. RequestBody와 차이점은 ModelAttribute는 요청 파라미터에만 적용이 된다. 그래서 Order를 쓸 때도 애노테이션을 생략해도 동작이 가능했음. (RequestBody는 불가능)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Jackson2HttpMessageConverter&lt;/code&gt; 로 인해 컨버전이 동작하는데 reflection 기반이기 때문에 객체에 &lt;code&gt;getter&lt;/code&gt;만 선언해도 동작이 됨&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;메소드레벨에서 &lt;code&gt;@ModelAttribute&lt;/code&gt; 사용&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;    @ModelAttribute(&amp;quot;orders&amp;quot;)
    public List&amp;lt;Order&amp;gt; orders(@AuthenticationPrincipal User user){

        Pageable pageable = PageRequest.of(0, orderProps.getPageSize());
        List&amp;lt;Order&amp;gt; orders = orderRepository.findByUserOrderByPlacedAtDesc(user, pageable);
        return orders;

    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;@ModelAttribute&lt;/code&gt;를 만약 메소드레벨에서 사용하면 지정한 변수명은 모든 요청에대해 자동으로 &lt;code&gt;model.setAttribute()&lt;/code&gt; 가 동작해서 model에 담기게 됨&lt;/strong&gt;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>@RequestParam</category>
      <category>스프링 annotation</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/159</guid>
      <comments>https://katastrophe.tistory.com/159#entry159comment</comments>
      <pubDate>Thu, 16 Jun 2022 21:37:19 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] YML 한번 알고 가기</title>
      <link>https://katastrophe.tistory.com/157</link>
      <description>&lt;h1&gt;&lt;b&gt;YML&lt;/b&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;YML 은 &amp;lsquo;사람이 쉽게 읽을 수 있는&amp;rsquo; 데이터 직렬화 양식이고 원래 Yet Another Markup Language 였는데 핵심은 마크업이 아닌 데이터임을 보여주기 위해 YAML Ain't Markup Language 라는 재귀적요소를 더한 이름으로 바꾸었음.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;YML의 장점&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가독성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자료형과의 결합&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주석 사용가능&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 가독성&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;xml의 경우엔 태그기반 markup으로 속성을 표시해 주는데 조금만 복잡해도 사람이 읽기가 힘들다. json 의 경우도 마찬가지로 어디까지가 Object이고 Array 인지 보기가 힘듬.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그에 반해&lt;u&gt; yml은 계층구조로 표현&lt;/u&gt;하여 가독성이 좋으며 .properties 에 비해 불필요한 중복제거도 가능.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/172022566-90366d9c-a9d0-45eb-a248-3c8a0d9214e9.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 자료형과의 결합&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;YML의 기본 자료형&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;스칼라 : number, String&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배열&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;Youtube:
 channel:
  - penbird
  - kyleschool
  - habithackers&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;&lt;b&gt;맵&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Youtube:
 channel:
  - name : penbird
    type : [&quot;keyboard and hand writings&quot;,&quot;keyboard and hand writings2&quot;]
  - name : kyleschool
    type : [&quot;data science and engineering&quot;, &quot;data science and engineering2&quot;]
  - name : habithackers
    type : [&quot;opct training&quot;, &quot;opct training2&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 주석사용가능&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#기호 하나로 간단하게 주석을 추가할 수 있음&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Youtube: # 유튜브
 channel: # 채널
  - name : penbird #이름
    type : [&quot;keyboard and hand writings&quot;,&quot;keyboard and hand writings2&quot;]
  - name : kyleschool
    type : [&quot;data science and engineering&quot;, &quot;data science and engineering2&quot;]
  - name : habithackers
    type : [&quot;opct training&quot;, &quot;opct training2&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Springboot에서의 YML&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Springboot 에서 구성속성들을 bean에 필요하면 추가로 매핑도 할 수 있음&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Component
@ConfigurationProperties(prefix = &quot;taco-orders&quot;)
@Data
@Validated
public class OrderProps {

    @Range(min=5,max=25,message = &quot;must be in 5~25&quot;)
    private int pageSize = 20;

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;taco:
  orders:
    pageSize: 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;Spring-configuration-metadata.json 처럼 json 포맷형식에 매핑되어 있거나&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;{
    .....
    {
      &quot;name&quot;: &quot;spring.profiles.active&quot;,
      &quot;type&quot;: &quot;java.util.List&amp;lt;java.lang.String&amp;gt;&quot;,
      &quot;description&quot;: &quot;Comma-separated list of active profiles. Can be overridden by a command line switch.&quot;,
      &quot;sourceType&quot;: &quot;org.springframework.boot.context.config.ConfigFileApplicationListener&quot;
    },
    {
      &quot;name&quot;: &quot;logging.level.values&quot;,
      &quot;values&quot;: [
        {
          &quot;value&quot;: &quot;trace&quot;
        },
        {
          &quot;value&quot;: &quot;debug&quot;
        },
        {
          &quot;value&quot;: &quot;info&quot;
        },
        {
          &quot;value&quot;: &quot;warn&quot;
        },
        {
          &quot;value&quot;: &quot;error&quot;
        },
        {
          &quot;value&quot;: &quot;fatal&quot;
        },
        {
          &quot;value&quot;: &quot;off&quot;
        }
      ],
      &quot;providers&quot;: [
        {
          &quot;name&quot;: &quot;any&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;@ConfigurationProperties 를 이용할 수도 있음&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;@ConfigurationProperties(prefix = &quot;spring.datasource&quot;)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

    private ClassLoader classLoader;

    /**
     * Whether to generate a random datasource name.
     */
    private boolean generateUniqueName = true;

    /**
     * Datasource name to use if &quot;generate-unique-name&quot; is false. Defaults to &quot;testdb&quot;
     * when using an embedded database, otherwise null.
     */
    private String name;

    /**
     * Fully qualified name of the connection pool implementation to use. By default, it
     * is auto-detected from the classpath.
     */
    private Class&amp;lt;? extends DataSource&amp;gt; type;

    /**
     * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
     */
    private String driverClassName;

    /**
     * JDBC URL of the database.
     */
    private String url;

    /**
     * Login username of the database.
     */
    private String username;

    /**
     * Login password of the database.
     */
    private String password;

.......&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;profile 분리&lt;/b&gt;&lt;/h2&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;application.yml&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
      url: jdbc:mariadb://localhost:3306/shop?serverTimezone=Asia/Seoul
      username: root
      password: 1234
      driver-class-name: org.mariadb.jdbc.Driver&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;application-prod.yml&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
      url: jdbc:mariadb://localhost:3306/shop?serverTimezone=Asia/Seoul
      username: root
      password: 1234
      driver-class-name: org.mariadb.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: none&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;application-test.yml&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
      url: jdbc:h2:tcp://localhost/D:/db/h2/taco
      username: sa
      password:
      driver-class-name: org.h2.Driver&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;properties vs yml&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분명 불 필요한 반복도 줄고 보기도 쉬운데 yml만 쓰면 좋아보인다. 하지만 단점도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:mariadb://localhost:3306/test?characterEncoding=UTF-8&amp;amp;serverTimezone=UTC
spring.datasource.hikari.username=root
spring.datasource.hikari.password=qlalfqjsgh
spring.datasource.hikari.maximum-pool-size=10&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
    driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
    url: jdbc:log4jdbc:mariadb://localhost:3306/test?characterEncoding=UTF-8&amp;amp;serverTimezone=UTC
    hikari:
      username: root
      password: qlalfqjsgh
      maximum-pool-size: 10&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;u&gt;프로퍼티 값&lt;/u&gt;을 불러 올 수 없음&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.properties 에서의 값은 @PropertySource로 불러올 수 있지만 기본적으로 .yml에서는 불가능.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Configuration 
@PropertySource(&quot;classpath:/com/taco/application.properties&quot;)
public class AppConfig{

    @Autowired 
    Environment env;

    @Bean
    public TestBean testBean(){
            TestBean testBean = new TestBean();
        testBean.setName(env.getProperty(&quot;testbean.name&quot;));
        return testBean;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;testbean.name = test&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;그 외 메세지 국제화 기능이나 예외 공통처리 등 .properties에서만 가능한 것들이 있음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, yml &amp;rArr; properties 로 변환하는 방법이 있음. (&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#boot-features-external-config&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고) 프로퍼티 우선순위&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저 홈 디렉토리에 있는 spring-boot-dev-tools.properties&lt;/li&gt;
&lt;li&gt;테스트에 있는 @TestPropertySource&lt;/li&gt;
&lt;li&gt;@SpringBootTest 애노테이션의 properties 애트리뷰트&lt;/li&gt;
&lt;li&gt;커맨드 라인 아규먼트&lt;/li&gt;
&lt;li&gt;SPRING_APPLICATION_JSON (환경 변수 또는 시스템 프로티) 에 들어있는 프로퍼티&lt;/li&gt;
&lt;li&gt;ServletConfig 파라미터&lt;/li&gt;
&lt;li&gt;ServletContext 파라미터&lt;/li&gt;
&lt;li&gt;java:comp/env JNDI 애트리뷰트&lt;/li&gt;
&lt;li&gt;System.getProperties() 자바 시스템 프로퍼티&lt;/li&gt;
&lt;li&gt;OS 환경 변수&lt;/li&gt;
&lt;li&gt;RandomValuePropertySource&lt;/li&gt;
&lt;li&gt;JAR 밖에 있는 특정 프로파일용 application properties&lt;/li&gt;
&lt;li&gt;JAR 안에 있는 특정 프로파일용 application properties&lt;/li&gt;
&lt;li&gt;JAR 밖에 있는 application properties&lt;/li&gt;
&lt;li&gt;JAR 안에 있는 application properties&lt;/li&gt;
&lt;li&gt;@PropertySource&lt;/li&gt;
&lt;li&gt;기본 프로퍼티 (SpringApplication.setDefaultProperties)&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Backend/Spring</category>
      <category>springboot YML</category>
      <category>yml이란</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/157</guid>
      <comments>https://katastrophe.tistory.com/157#entry157comment</comments>
      <pubDate>Tue, 7 Jun 2022 01:21:27 +0900</pubDate>
    </item>
    <item>
      <title>[SpringSecurity] 스프링 시큐리티 구성하기</title>
      <link>https://katastrophe.tistory.com/156</link>
      <description>&lt;h1&gt;Spring Secutiry 구성하기&lt;/h1&gt;
&lt;h3&gt;WebSecurityConfigurerAdapter&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;SpringSecutiry 의존성을 추가하게 되면 기본적으로 &lt;code&gt;WebSecurityConfigurerAdapter&lt;/code&gt; 클래스가 실행.&lt;br&gt;&lt;code&gt;WebSecurityConfigurerAdapter&lt;/code&gt; 는 SpringSecutiry 의 웹(http) 보안 기능 초기화 및 설정들을 당담하는 내용이 담겨있고 인증/인가 관련 설정을 제공.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/170840377-7d349cc9-87bc-4a48-abef-b225db0c18e3.png&quot; alt=&quot;Untitled&quot;&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;configure() 메서드를 Override 하여 설정정보를 구성할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;
    @Bean
    public PasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }

    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers(&amp;quot;/design&amp;quot;, &amp;quot;/orders&amp;quot;)
                .access(&amp;quot;hasRole(&amp;#39;ROLE_USER&amp;#39;)&amp;quot;)
                .antMatchers(&amp;quot;/&amp;quot;, &amp;quot;/**&amp;quot;).access(&amp;quot;permitAll&amp;quot;)
                .and()
                .formLogin()
                .loginPage(&amp;quot;/login&amp;quot;)
                .and()
                .logout()
                .logoutSuccessUrl(&amp;quot;/&amp;quot;)
                .and()
                .csrf();
    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(encoder());
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;참고 (구성 메소드)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메소드&lt;/th&gt;
&lt;th&gt;하는 일&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;access(String)&lt;/td&gt;
&lt;td&gt;인자로 전달된 SpEL 표현식이 true면 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;anonymous()&lt;/td&gt;
&lt;td&gt;익명의 사용자에게 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;authenticated()&lt;/td&gt;
&lt;td&gt;익명이 아닌 사용자로 인증된 경우 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;denyAll()&lt;/td&gt;
&lt;td&gt;무조건 접근을 거부한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fullyAuthenticated()&lt;/td&gt;
&lt;td&gt;익명이 아니거나 또는 remember-me가 아닌 사용자로 인증되면 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasAnyAuthority(String...)&lt;/td&gt;
&lt;td&gt;지정된 권한 중 어떤 것이라도 사용자가 갖고 있으면 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasAnyRole(String...)&lt;/td&gt;
&lt;td&gt;지정된 역할 중 어느 하나라도 사용자가 갖고 있으면 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasAuthority(String)&lt;/td&gt;
&lt;td&gt;지정된 권한을 사용자가 갖고 있으면 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasIpAddress(String)&lt;/td&gt;
&lt;td&gt;지정된 IP 주소로부터 요청이 오면 접근을 하용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasRole(String)&lt;/td&gt;
&lt;td&gt;지정된 역할을 사용자가 갖고 있으면 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;not()&lt;/td&gt;
&lt;td&gt;다른 접근 메소드들의 효력을 무효화한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;permitAll()&lt;/td&gt;
&lt;td&gt;무조건 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rememberMe()&lt;/td&gt;
&lt;td&gt;remember-me(이전 로그인 정보를 쿠키나 데이터베이스로 저장한 후 일정 기간 내에 다시 접근 시 저장된 정보로 자동 로그인됨)를 통해 인증된 사용자의 접근을 허용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;authentication&lt;/th&gt;
&lt;th&gt;해당 사용자의 인증 객체&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;denyAll&lt;/td&gt;
&lt;td&gt;항상 false를 산출한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasAnyRole(역할 내역)&lt;/td&gt;
&lt;td&gt;지정된 역할 중 어느 하나라도 해당 사용자가 갖고 있으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasRole(역할)&lt;/td&gt;
&lt;td&gt;지정된 역할을 해당 사용자가 갖고 있으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasIpAddress(IP 주소)&lt;/td&gt;
&lt;td&gt;지정된 IP 주소로부터 해당 요청이 온 것이면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isAnonymous()&lt;/td&gt;
&lt;td&gt;해당 사용자가 익명 사용자이면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isAuthenticated()&lt;/td&gt;
&lt;td&gt;해당 사용자가 익명이 아닌 사용자로 인증되었으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isFullyAuthenticated()&lt;/td&gt;
&lt;td&gt;해당 사용자가 익명이 아니거나 또는 remember-me가 아닌 사용자로 인증 되었으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isRememberMe()&lt;/td&gt;
&lt;td&gt;해당 사용자가 remember-me 기능으로 인증되었으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;permitAll&lt;/td&gt;
&lt;td&gt;항상 true를 산출한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;principal&lt;/td&gt;
&lt;td&gt;해당 사용자의 principal 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;사용자 스토어 구성&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;In-Memory 기반&lt;/li&gt;
&lt;li&gt;JDBC 기반&lt;/li&gt;
&lt;li&gt;LDAP 기반&lt;/li&gt;
&lt;li&gt;커스텀 사용자 명세 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;In-Memory 기반&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;사용자 정보를 유지/관리 할 수 있는 곳 중 하나가 메모리&lt;br&gt;변경이 필요없는 사용자를 정해놓는 용도로 사용 ⇒ 테스트 목적&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser(&amp;quot;admin&amp;quot;)
                .password(&amp;quot;{noop}1234&amp;quot;)
                .authorities(&amp;quot;ADMIN&amp;quot;)
                .and()
                .withUser(&amp;quot;testuser&amp;quot;)
                .password(&amp;quot;{noop}1234&amp;quot;)
                .authorities(&amp;quot;USER&amp;quot;);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;JDBC 기반&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;사용자 정보를 RDBMS로 유지/관리 하려는 목적으로 사용.&lt;br&gt;기본으로 제공하는 Query를 커스터마이징 가능&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;기본적으로 제공되는 쿼리로 가져오는 값 (username으로 검색)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자 검색 :  username, password, enabled&lt;/li&gt;
&lt;li&gt;권한 검색 : username, authority&lt;/li&gt;
&lt;li&gt;그룹 검색 : groupid, groupname, groupautority&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery(&amp;quot;&amp;quot;&amp;quot;
                        select username, password, enabled 
                        from users 
                        where username=?
                        &amp;quot;&amp;quot;&amp;quot;)
                .authoritiesByUsernameQuery(&amp;quot;&amp;quot;&amp;quot;
                        select username, authority 
                        from authorities 
                        where username=?
                        &amp;quot;&amp;quot;&amp;quot;)
                .passwordEncoder(new NoEncodingPasswordEncoder());
    }
     */&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;LDAP 기반&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lightweight Directory Access Protocol&lt;/strong&gt;  : 네트워크 상의 디렉토리 서비스 표준인 X.500의 DAP를 기반으로한 경량화(Lightweight) 된 DAP 버전으로 TCP/IP 계층과 관련되어있음.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;주로 사용자, 시스템, 네트워크, 서비스, 애플리케이션 등의 정보를 트리 구조로 저장하여 조회하거나 관리하는 용도나 특정 영역에서 이용자명과 패스워드를 확인하여 인증하는 용도.&lt;/p&gt;
&lt;p&gt;관련 기술블로그 : &lt;a href=&quot;https://www.blocko.io/developer/ldap-%EC%9D%B8%EC%A6%9D/&quot;&gt;https://www.blocko.io/developer/ldap-인증/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.ldapAuthentication()
                .userSearchBase(&amp;quot;ou=people&amp;quot;) // 사용자를 찾기위한 기준점 쿼리
                .userSearchFilter(&amp;quot;(uid={0})&amp;quot;) // 사용자를 찾기위한 필터
                .groupSearchBase(&amp;quot;ou=groups&amp;quot;) // 그룹을 찾기위한 기준점 쿼리
                .groupSearchFilter(&amp;quot;member={0}&amp;quot;) // 그룹을 찾기위한 필터
                .contextSource()
                .root(&amp;quot;dc=taco,dc=com&amp;quot;) // ldap 서버 루트 경로 지정
                .ldif(&amp;quot;classpath:users.ldif&amp;quot;) // ldif 파일을 지정
                .and()
                .passwordCompare()
                .passwordEncoder(new BCryptPasswordEncoder())
                .passwordAttribute(&amp;quot;userPasscode&amp;quot;);

    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;커스텀 사용자 명세 서비스&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;스프링에 내장된 사용자 스토어 외 사용자 명세 서비스를 커스터마이징해서 사용이 가능&lt;br&gt;&lt;code&gt;UserDetails&lt;/code&gt; 를 구현받는 엔티티를 생성해야함.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;
import java.io.Serial;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;

@Entity
@Data
@NoArgsConstructor(access=AccessLevel.PUBLIC, force=true)
@RequiredArgsConstructor
public class User implements UserDetails {
    private static final long serialVersionUID = 1L;

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

    private final String username;
    private final String password;
    private final String fullname;
    private final String street;
    private final String city;
    private final String state;
    private final String zip;
    private final String phoneNumber;

    @Override
    public Collection&amp;lt;? extends GrantedAuthority&amp;gt; getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority(&amp;quot;ROLE_USER&amp;quot;));
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;참고: UserDetails&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메소드명&lt;/th&gt;
&lt;th&gt;리턴타입&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;getAuthorities()&lt;/td&gt;
&lt;td&gt;Collection&amp;lt;? extends   GrantedAuthority&amp;gt;&lt;/td&gt;
&lt;td&gt;계정이 갖고있는 권한 목록을 리턴한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;getPassword()&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;계정의 비밀번호를 리턴한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;getUsername()&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;계정의 이름을 리턴한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isAccountNonExpired()&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;계정이 만료되지 않았는 지 리턴한다. (true: 만료안됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isAccountNonLocked()&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;계정이 잠겨있지 않았는 지 리턴한다. (true: 잠기지 않음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isCredentialNonExpired()&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;비밀번호가 만료되지 않았는 지 리턴한다. (true: 만료안됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isEnabled()&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;계정이 활성화(사용가능)인 지 리턴한다. (true: 활성화)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
@RequiredArgsConstructor
public class UserRepositoryUserDetailsService implements UserDetailsService {
    private final UserRepository userRepo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = userRepo.findByUsername(username);
        if (user != null) {
            return user;
        }
        throw new UsernameNotFoundException(&amp;quot;User &amp;#39;&amp;quot; + username + &amp;quot;&amp;#39; not found&amp;quot;);
    }
}

@Repository
public interface UserRepository extends JpaRepository&amp;lt;User,Long&amp;gt; {

    User findByUsername(String username);
}

        @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers(&amp;quot;/design&amp;quot;, &amp;quot;/orders&amp;quot;)
                .access(&amp;quot;hasRole(&amp;#39;ROLE_USER&amp;#39;)&amp;quot;)
                .antMatchers(&amp;quot;/&amp;quot;, &amp;quot;/**&amp;quot;).access(&amp;quot;permitAll&amp;quot;)
                .and()
                .formLogin()
                .loginPage(&amp;quot;/login&amp;quot;)
                .and()
                .logout()
                .logoutSuccessUrl(&amp;quot;/&amp;quot;)
                .and()
                .csrf();
    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(encoder());
    }

@Data
public class RegistrationForm {

    private String username;
    private String password;
    private String fullname;
    private String street;
    private String city;
    private String state;
    private String zip;
    private String phone;

    public User toUser(PasswordEncoder passwordEncoder) {
        return new User(username,passwordEncoder.encode(password),fullname, street, city, state, zip, phone);
    }
}

@Controller
@RequestMapping(&amp;quot;/register&amp;quot;)
@RequiredArgsConstructor
public class RegistrationController {
    private final UserRepository userRepo;
    private final PasswordEncoder passwordEncoder;

    @GetMapping
    public String registerForm() {
        return &amp;quot;registration&amp;quot;;
    }

    @PostMapping
    public String processRegistration(RegistrationForm form) {
        userRepo.save(form.toUser(passwordEncoder));
        return &amp;quot;redirect:/login&amp;quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;password 암호화&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;SpringSecurity5부터 의무적으로 &lt;code&gt;PasswordEncoder&lt;/code&gt;를 이용하여 암호화를 하지 않으면 오류가 발생함.  암호화를 하면 DB에 저장될 때 암호화된 password가 저장이 됨.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;암호화된 사용자정보&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;CITY&lt;/th&gt;
&lt;th&gt;FULLNAME&lt;/th&gt;
&lt;th&gt;PASSWORD&lt;/th&gt;
&lt;th&gt;PHONE_NUMBER&lt;/th&gt;
&lt;th&gt;STATE&lt;/th&gt;
&lt;th&gt;STREET&lt;/th&gt;
&lt;th&gt;USERNAME&lt;/th&gt;
&lt;th&gt;ZIP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;aaa&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;td&gt;$2a$10$GtCiKop4o9Vlm7CoKryHCuXVehCyNYB5krnMJBbfOylkVInzinc7K&lt;/td&gt;
&lt;td&gt;aaa&lt;/td&gt;
&lt;td&gt;aaa&lt;/td&gt;
&lt;td&gt;aaa&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;td&gt;aaa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;SpringSecutiry 암호화 모듈&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BCryptPasswordEncoder&lt;/code&gt; : bcrypt 해싱&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NoOpPasswordEncoder&lt;/code&gt; : 암호화 x&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pbkdf2PasswordEncoder&lt;/code&gt; : PBKDF2 암호화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SCryptPasswordEncoder&lt;/code&gt; : scrypt 해싱&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StandardPasswordEncoder&lt;/code&gt; : SHA-256&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class NoEncodingPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence rawPwd) {
        return rawPwd.toString();
    }

    @Override
    public boolean matches(CharSequence rawPwd, String encodedPwd) {
        return rawPwd.toString().equals(encodedPwd);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PasswordEncoder 의 encode 와 matches 메서드를 이용하여 암호화/비교를 수행함.&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>spring security configure</category>
      <category>스프링</category>
      <category>스프링시큐리티 구성</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/156</guid>
      <comments>https://katastrophe.tistory.com/156#entry156comment</comments>
      <pubDate>Tue, 7 Jun 2022 01:18:21 +0900</pubDate>
    </item>
    <item>
      <title>[SpringSecurity] 권한 설정 hasRole 오류와 prefix</title>
      <link>https://katastrophe.tistory.com/155</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;&lt;b&gt;스프링시큐리티를 공부하던 도중 책에서 이런 코드가 있었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SecutiryConfig 구성 클래스 안에서 configure를 orverride 하는 코드이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqF42M/btrDHd8TZJT/pBk030dTBeXK3r92hmzbIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqF42M/btrDHd8TZJT/pBk030dTBeXK3r92hmzbIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqF42M/btrDHd8TZJT/pBk030dTBeXK3r92hmzbIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqF42M%2FbtrDHd8TZJT%2FpBk030dTBeXK3r92hmzbIK%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;590&quot; height=&quot;176&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;176&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;.hasRole() 을 호출할 때 ROLE_USER 로 설정하는것.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;228&quot; data-origin-height=&quot;23&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XQEnK/btrDGdn1DhO/8TykofG1ihMuKAQP5KO4l1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XQEnK/btrDGdn1DhO/8TykofG1ihMuKAQP5KO4l1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XQEnK/btrDGdn1DhO/8TykofG1ihMuKAQP5KO4l1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXQEnK%2FbtrDGdn1DhO%2F8TykofG1ihMuKAQP5KO4l1%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;228&quot; height=&quot;23&quot; data-origin-width=&quot;228&quot; data-origin-height=&quot;23&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2427&quot; data-origin-height=&quot;949&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4tQgA/btrDJvnFpii/wa4kJzq5vEwQSWzjpLgs90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4tQgA/btrDJvnFpii/wa4kJzq5vEwQSWzjpLgs90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4tQgA/btrDJvnFpii/wa4kJzq5vEwQSWzjpLgs90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4tQgA%2FbtrDJvnFpii%2Fwa4kJzq5vEwQSWzjpLgs90%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;2427&quot; height=&quot;949&quot; data-origin-width=&quot;2427&quot; data-origin-height=&quot;949&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;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmg82l/btrDLVMVUkP/pPeWpspU7xc9tUv3uPBSu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmg82l/btrDLVMVUkP/pPeWpspU7xc9tUv3uPBSu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmg82l/btrDLVMVUkP/pPeWpspU7xc9tUv3uPBSu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcmg82l%2FbtrDLVMVUkP%2FpPeWpspU7xc9tUv3uPBSu1%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;1043&quot; height=&quot;64&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;ROLE_ 은 자동생성해주니깐 prefix의 ROLE_ 을 쓰지말라고 ..&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;hasRole()의 내부는 이렇게 생겨먹었다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb1oHk/btrDGJtthv7/q3UuoeYBUmK9fGtBbQbBN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb1oHk/btrDGJtthv7/q3UuoeYBUmK9fGtBbQbBN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb1oHk/btrDGJtthv7/q3UuoeYBUmK9fGtBbQbBN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb1oHk%2FbtrDGJtthv7%2Fq3UuoeYBUmK9fGtBbQbBN0%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;1003&quot; height=&quot;159&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;저 rolePrefix는 자동으로 ROLE_ 이라는 prefix를 붙여주기 때문에 ROLE_ 이라는 prefix를 제외한 권한을 적으라는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1397&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddkc2Y/btrDIovzwKZ/sWksIX1cwnbSXiBxCXbuO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddkc2Y/btrDIovzwKZ/sWksIX1cwnbSXiBxCXbuO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddkc2Y/btrDIovzwKZ/sWksIX1cwnbSXiBxCXbuO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddkc2Y%2FbtrDIovzwKZ%2FsWksIX1cwnbSXiBxCXbuO1%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;1397&quot; height=&quot;441&quot; data-origin-width=&quot;1397&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 ROLE_ 이라는 prefix를 자동으로 붙여준다. 별도의 설정을 하지 않는 이상&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 class=&quot;less&quot;&gt;&lt;code&gt;    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers(&quot;/design&quot;, &quot;/orders&quot;)
                    .hasRole(&quot;USER&quot;)
                    .antMatchers(&quot;/&quot;, &quot;/**&quot;).permitAll()
                .and()
                    .formLogin()
                    .loginPage(&quot;/login&quot;)
//                    .defaultSuccessUrl(&quot;/design&quot;,true)
                .and()
                    .logout()
                    .logoutSuccessUrl(&quot;/&quot;)
                .and()
                    .csrf().disable();
    }&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;그런데 만약 내가 prefix를 따로 설정 해놨다면 ?&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getAuthorities에서 설정한 prefix를 따온다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Entity
@Data
@NoArgsConstructor(access=AccessLevel.PUBLIC, force=true)
@RequiredArgsConstructor
public class User implements UserDetails {
    private static final long serialVersionUID = 1L;

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

    private final String username;
    private final String password;
    private final String fullname;
    private final String street;
    private final String city;
    private final String state;
    private final String zip;
    private final String phoneNumber;


    @Override
    public Collection&amp;lt;? extends GrantedAuthority&amp;gt; getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority(&quot;ROLE_USER&quot;));
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Spring</category>
      <category>스프링시큐리티 hasrole</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/155</guid>
      <comments>https://katastrophe.tistory.com/155#entry155comment</comments>
      <pubDate>Thu, 2 Jun 2022 01:35:33 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1062] 가르침 JAVA</title>
      <link>https://katastrophe.tistory.com/154</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1062&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1062&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1654016204873&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;1062번: 가르침&quot; data-og-description=&quot;첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1062&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1062&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vLoqY/hyOBKNROOI/ZsHL3Jt2JWafJh8P1auBe1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1062&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1062&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vLoqY/hyOBKNROOI/ZsHL3Jt2JWafJh8P1auBe1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1062번: 가르침&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1279&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MKuJr/btrDInh8rlV/ZYtTsHX7Q6MfSgb1T7caG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MKuJr/btrDInh8rlV/ZYtTsHX7Q6MfSgb1T7caG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MKuJr/btrDInh8rlV/ZYtTsHX7Q6MfSgb1T7caG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMKuJr%2FbtrDInh8rlV%2FZYtTsHX7Q6MfSgb1T7caG1%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;1279&quot; height=&quot;770&quot; data-origin-width=&quot;1279&quot; data-origin-height=&quot;770&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;1209&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZuXtz/btrDHeFK5Cg/g5aiRcrzZN7iOZPS0bQWTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZuXtz/btrDHeFK5Cg/g5aiRcrzZN7iOZPS0bQWTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZuXtz/btrDHeFK5Cg/g5aiRcrzZN7iOZPS0bQWTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZuXtz%2FbtrDHeFK5Cg%2Fg5aiRcrzZN7iOZPS0bQWTK%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;1209&quot; height=&quot;906&quot; data-origin-width=&quot;1209&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단순히 문자열 완전탐색으로 풀 수 있었다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우선, anta tica 부터 5개의 알파벳을 차지하고 시작하므로 k&amp;gt;5 인경우에만 진행해야 한다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;pattern matching 함수를 사용하여 만약, 해당 패턴의 단어가 있으면 anta tica를 제외한 부분만&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문자열 리스트에 추가한다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;알파벳 방문 체크 배열 visit 을 하나씩 체크하다가 k 개의 갯수가 되었다면 문자열 리스트를 순회하여 모두 읽을 수 있는지 체크&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1654016480587&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.*;

import static java.util.Arrays.stream;

public class Main {


    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static int n,k;
    static int ans=0;
    static String[] words;
    static boolean[] visit = new boolean[26];
    public static void main(String[] args) throws IOException {
        String[] input = br.readLine().split(&quot; &quot;);
        n = Integer.parseInt(input[0]);
        k = Integer.parseInt(input[1]);

        visit['a'-'a']=true;
        visit['n'-'a']=true;
        visit['t'-'a']=true;
        visit['i'-'a']=true;
        visit['c'-'a']=true;

        if(k&amp;lt;5){
            System.out.println(ans);
            return;
        }
        words = new String[n];
        k = k-5;
        Pattern pattern = Pattern.compile(&quot;anta(.*)tica&quot;);
        for(int i=0;i&amp;lt;n;i++){
            Matcher matcher = pattern.matcher(br.readLine());
            if(matcher.find())
                words[i] = matcher.group(1);
        }

        dfs(0,0);
        System.out.println(ans);
    }

    static void dfs(int cur,int len){

        if(len == k){
            int cnt = 0;

            for(int i=0;i&amp;lt;n;i++){
                boolean read = true;

                for(int j = 0; j&amp;lt; words[i].length(); j++){

                    if (!visit[words[i].charAt(j) - 'a']) {
                        read = false;
                        break;
                    }
                }
                if(read)
                    cnt++;
            }

            ans = Math.max(ans,cnt);
            return;
        }

        for(int i=cur;i&amp;lt;26;i++){
            if(!visit[i]){
                visit[i]=true;
                dfs(i,len+1);
                visit[i]=false;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>문자열</category>
      <category>백준 1062 java</category>
      <category>백준 가르침 java</category>
      <category>완전탐색</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/154</guid>
      <comments>https://katastrophe.tistory.com/154#entry154comment</comments>
      <pubDate>Wed, 1 Jun 2022 02:01:38 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [2573] 빙산 JAVA</title>
      <link>https://katastrophe.tistory.com/153</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2573&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/2573&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1653838779132&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;2573번: 빙산&quot; data-og-description=&quot;첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2573&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2573&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PhwPp/hyOzTjJj47/okdt8CgrTP4R1A3yEFk8z1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2573&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2573&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PhwPp/hyOzTjJj47/okdt8CgrTP4R1A3yEFk8z1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;2573번: 빙산&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1223&quot; data-origin-height=&quot;911&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2uCw/btrDknxOXT6/KX9y8C8GAWycW9Qz4hPmg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2uCw/btrDknxOXT6/KX9y8C8GAWycW9Qz4hPmg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2uCw/btrDknxOXT6/KX9y8C8GAWycW9Qz4hPmg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2uCw%2FbtrDknxOXT6%2FKX9y8C8GAWycW9Qz4hPmg1%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;1223&quot; height=&quot;911&quot; data-origin-width=&quot;1223&quot; data-origin-height=&quot;911&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;1205&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crL4s4/btrDowAkN6C/Mh5OU2Kf07osWdVONhLz90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crL4s4/btrDowAkN6C/Mh5OU2Kf07osWdVONhLz90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crL4s4/btrDowAkN6C/Mh5OU2Kf07osWdVONhLz90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrL4s4%2FbtrDowAkN6C%2FMh5OU2Kf07osWdVONhLz90%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;1205&quot; height=&quot;778&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;778&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;1189&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtsy8b/btrDnj2hu9p/IrrPbMkKvjF2w8gpZL1OC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtsy8b/btrDnj2hu9p/IrrPbMkKvjF2w8gpZL1OC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtsy8b/btrDnj2hu9p/IrrPbMkKvjF2w8gpZL1OC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtsy8b%2FbtrDnj2hu9p%2FIrrPbMkKvjF2w8gpZL1OC0%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;1189&quot; height=&quot;400&quot; data-origin-width=&quot;1189&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;탐색문제인데 , 탐색할 때 마다 다른 노드에 영향을 끼치는 유형이며 큐를 이용해야 하는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;isSeprated() 함수로 분리가 되었는지에 대한 검사를 계속 시행하면서&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;만약 분리가 되지 않은 경우 전 좌표를 살피면서 동서남북으로 바다가 있는지 검사한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;검사한 결과가 0보다 클 경우 큐에 삽입.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;if 큐가 비어있을 경우 =&amp;gt; 분리 불가능. break&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653839076874&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;
import java.util.stream.IntStream;

import static java.util.Arrays.stream;


public class Main {

    static int[] dx={0,0,1,-1},dy={1,-1,0,0};
    static int w,h;
    static int[][] map;
    static boolean[][] visit;

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String[] args) throws IOException {
        StringTokenizer st = new StringTokenizer(br.readLine(),&quot; &quot;);

        h = Integer.parseInt(st.nextToken());
        w = Integer.parseInt(st.nextToken());
        map = new int[h][w];
        for(int i=0;i&amp;lt;h;i++){
            map[i] = stream(br.readLine().split(&quot; &quot;))
                                .mapToInt(Integer::parseInt)
                                .toArray();

        } // end input

        int answer=0;

        while (!isSeparate()){

            LinkedList&amp;lt;int[]&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();

            for(int i=0;i&amp;lt;h;i++){
                for(int j=0;j&amp;lt;w;j++){
                    if(map[i][j]&amp;gt;0){
                        q.add(new int[]{j,i, countNearBy(j, i)}); // x,y,count
                    }
                }
            }

            if (q.isEmpty()) {
                answer = 0;
                break;
            }

            while (!q.isEmpty()){
                int[] cur = q.poll();
                int x = cur[0];
                int y = cur[1];
                int cnt = cur[2];
                map[y][x] = map[y][x] &amp;gt; cnt? map[y][x]-cnt : 0;
            }
            answer++;
        }

        System.out.println(answer);

    }

    static boolean isRange(int x,int y){

        return x&amp;gt;=0&amp;amp;&amp;amp;y&amp;gt;=0&amp;amp;&amp;amp;x&amp;lt;w&amp;amp;&amp;amp;y&amp;lt;h;
    }
    static int countNearBy(int x,int y){

        return (int) IntStream.range(0, 4)
                .filter(i -&amp;gt; isRange(x, y))
                .filter(i-&amp;gt;map[y + dy[i]][x + dx[i]] == 0)
                .count();
    }
    static boolean isSeparate(){

        visit = new boolean[h][w];
        int cnt=0;
        for(int i=0;i&amp;lt;h;i++){
            for(int j=0;j&amp;lt;w;j++){
                if(!visit[i][j] &amp;amp;&amp;amp; map[i][j]!=0) {
                    dfs(j, i, visit);
                    cnt++;
                }
            }
        }
        return cnt&amp;gt;1;
    }

    static void dfs(int x,int y,boolean[][] visit){

        visit[y][x]=true;

        for(int i=0;i&amp;lt;dx.length;i++){

            int nextX = x+dx[i];
            int nextY = y+dy[i];

            if(isRange(nextX,nextY) &amp;amp;&amp;amp; map[nextY][nextX]!=0 &amp;amp;&amp;amp; !visit[nextY][nextX])
                dfs(nextX,nextY,visit);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 2573 빙산</category>
      <category>백준 빙산 java</category>
      <category>탐색</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/153</guid>
      <comments>https://katastrophe.tistory.com/153#entry153comment</comments>
      <pubDate>Mon, 30 May 2022 00:45:00 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [13458] 시험감독 JAVA</title>
      <link>https://katastrophe.tistory.com/152</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13458&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/13458&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1653838393996&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;13458번: 시험 감독&quot; data-og-description=&quot;첫째 줄에 시험장의 개수 N(1 &amp;le; N &amp;le; 1,000,000)이 주어진다. 둘째 줄에는 각 시험장에 있는 응시자의 수 Ai (1 &amp;le; Ai &amp;le; 1,000,000)가 주어진다. 셋째 줄에는 B와 C가 주어진다. (1 &amp;le; B, C &amp;le; 1,000,000)&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/13458&quot; data-og-url=&quot;https://www.acmicpc.net/problem/13458&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/feVOD/hyOzNqgRGb/Xtx5kSRuJuDq8rncckcT30/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13458&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/13458&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/feVOD/hyOzNqgRGb/Xtx5kSRuJuDq8rncckcT30/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;13458번: 시험 감독&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 시험장의 개수 N(1 &amp;le; N &amp;le; 1,000,000)이 주어진다. 둘째 줄에는 각 시험장에 있는 응시자의 수 Ai (1 &amp;le; Ai &amp;le; 1,000,000)가 주어진다. 셋째 줄에는 B와 C가 주어진다. (1 &amp;le; B, C &amp;le; 1,000,000)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9kQyX/btrDoVgxyXN/BkjNIjW25LgEZTvHl4Avkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9kQyX/btrDoVgxyXN/BkjNIjW25LgEZTvHl4Avkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9kQyX/btrDoVgxyXN/BkjNIjW25LgEZTvHl4Avkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9kQyX%2FbtrDoVgxyXN%2FBkjNIjW25LgEZTvHl4Avkk%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;1203&quot; height=&quot;784&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;784&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;1197&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oZW0s/btrDqV1rwaq/BUnpoiIYlYzKEEaHXgUDq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oZW0s/btrDqV1rwaq/BUnpoiIYlYzKEEaHXgUDq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZW0s/btrDqV1rwaq/BUnpoiIYlYzKEEaHXgUDq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZW0s%2FbtrDqV1rwaq%2FBUnpoiIYlYzKEEaHXgUDq1%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;1197&quot; height=&quot;727&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;727&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 각 시험장 마다 총 감독원이 꼭 있어야 함&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 시험장 인원을 부감독 수로 나눔&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653838646471&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static java.util.Arrays.stream;


public class Main {



    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        int[] arr = stream(br.readLine().split(&quot; &quot;))
                            .mapToInt(Integer::parseInt).sorted()
                            .toArray();


        int[] input = stream(br.readLine().split(&quot; &quot;))
                            .mapToInt(Integer::parseInt)
                            .toArray();
        int b = input[0];
        int c = input[1];

        long answer = 0;
        for(int i=0;i&amp;lt;n;i++){

            arr[i] -= b;
            answer++;

            if(arr[i]&amp;lt;0)
                continue;

            answer+=arr[i]/c;

            if(arr[i]%c&amp;gt;0)
                answer++;
        }

        System.out.println(answer);
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 13458 java</category>
      <category>백준 시험감독 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/152</guid>
      <comments>https://katastrophe.tistory.com/152#entry152comment</comments>
      <pubDate>Mon, 30 May 2022 00:37:50 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [17136] 색종이붙이기 JAVA</title>
      <link>https://katastrophe.tistory.com/151</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17136&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/17136&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1653410872062&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;17136번: 색종이 붙이기&quot; data-og-description=&quot;&amp;lt;그림 1&amp;gt;과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는&amp;nbsp;1&amp;times;1, 2&amp;times;2, 3&amp;times;3, 4&amp;times;4, 5&amp;times;5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. &amp;lt;그림 1&amp;gt; 색종이를 크&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/17136&quot; data-og-url=&quot;https://www.acmicpc.net/problem/17136&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/s0w3m/hyOvQg8qPi/ghIkgCdcslxQgGpbAOppwK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/fAbZn/hyOvOp57sH/cnRRlSkbrA9JfW36ip1zmK/img.png?width=1943&amp;amp;height=604&amp;amp;face=0_0_1943_604&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17136&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/17136&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/s0w3m/hyOvQg8qPi/ghIkgCdcslxQgGpbAOppwK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/fAbZn/hyOvOp57sH/cnRRlSkbrA9JfW36ip1zmK/img.png?width=1943&amp;amp;height=604&amp;amp;face=0_0_1943_604');&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;17136번: 색종이 붙이기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;그림 1&amp;gt;과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는&amp;nbsp;1&amp;times;1, 2&amp;times;2, 3&amp;times;3, 4&amp;times;4, 5&amp;times;5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. &amp;lt;그림 1&amp;gt; 색종이를 크&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLZpWM/btrC4g5rLEM/QTISrYNJU4gOIhQ419oBp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLZpWM/btrC4g5rLEM/QTISrYNJU4gOIhQ419oBp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLZpWM/btrC4g5rLEM/QTISrYNJU4gOIhQ419oBp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLZpWM%2FbtrC4g5rLEM%2FQTISrYNJU4gOIhQ419oBp0%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;1245&quot; height=&quot;826&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;826&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;1192&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkQmW4/btrC1iw4pHS/mmkmXRYtKmok6erkfo53Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkQmW4/btrC1iw4pHS/mmkmXRYtKmok6erkfo53Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkQmW4/btrC1iw4pHS/mmkmXRYtKmok6erkfo53Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkQmW4%2FbtrC1iw4pHS%2FmmkmXRYtKmok6erkfo53Kk%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;1192&quot; height=&quot;233&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;이 문제의 핵심은 그리디하게 접근해서는 풀 수 없는 문제이다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;반례)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 0 0 0 0 0 0 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 1 1 1 1 1 1 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 1 1 1 1 1 1 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 1 1 1 1 1 1 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 1 1 1 1 1 1 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 1 1 1 1 1 1 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 1 1 1 1 1 1 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 0 0 0 0 0 0 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 0 0 0 0 0 0 0 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;0 0 0 0 0 0 0 0 0 0&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;답은 4인데, 큰 것 부터 차례대로 붙이면 4보다 많은 갯수가 나온다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;따라서 완전탐색을 진행해야한다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 주어진 좌표에서 1 찾기&lt;/b&gt;&lt;br /&gt;&lt;b&gt;2. 해당 좌표(top-left) 기준으로 사이즈마다 모두 가능한지 검사하기&lt;/b&gt;&lt;br /&gt;&lt;b&gt;3. 2번이 가능하다면 해당 좌표를 -1로 두고 1번을 반복(dfs)&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;4. 3번의 과정이 끝이나면 백트래킹&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653411187999&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static java.util.Arrays.stream;


public class Main {

    static int[][] map = new int[10][10];
    static int[] arr = new int[6];
    static int min = 26;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        for(int i=0;i&amp;lt;10;i++){
            map[i] = stream(br.readLine().split(&quot; &quot;))
                                .mapToInt(Integer::parseInt)
                                .toArray();
        }
        Arrays.fill(arr,5);
        arr[0]=0;

        dfs();

        System.out.println(min&amp;gt;25?-1:min);
    }

    private static void dfs() {

        int[] point = find();

        if(point[0] == -1 &amp;amp;&amp;amp; point[1]==-1){ // 최소값 갱신 or 종결조건:탐색 끝좌표
            int cnt = 25 - stream(arr).sum();
            min = Math.min(min, cnt);
            return;
        }

        int x = point[0];
        int y = point[1];


        for (int size = 5; size &amp;gt;= 1; size--) {
            if (arr[size] &amp;gt; 0 &amp;amp;&amp;amp; canAttach(x, y, size)) {
                attach(x, y, size); //붙이기
                dfs();
                attach(x, y, size); //떼기
            }
        }

    }

    private static int[] find() { // 좌표찾기

        for(int i=0;i&amp;lt;10;i++){
            for(int j=0;j&amp;lt;10;j++){
                if(map[i][j]==1)
                    return new int[]{j,i};
            }
        }
        return new int[]{-1,-1};
    }

    private static void attach(int x, int y, int size) {
        for (int i = y; i &amp;lt; y + size; i++) {
            for (int j = x; j &amp;lt; x + size; j++) {
                map[i][j]*=-1;
            }
        }

        arr[size] += map[y][x];
    }

    private static boolean canAttach(int x, int y, int size) {
        for(int i=y;i&amp;lt;y+size;i++){
            for(int j=x;j&amp;lt;x+size;j++){
                if(!isRange(j,i) || map[i][j]!=1) // 0: 못붙이는자리 -1: 이미 붙여진 자리
                    return false;
            }
        }
        return true;
    }

    private static boolean isRange(int x, int y) {
        return x&amp;gt;=0&amp;amp;&amp;amp;y&amp;gt;=0&amp;amp;&amp;amp;x&amp;lt;10&amp;amp;&amp;amp;y&amp;lt;10;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T1NcU/btrC2CA9X1e/gIUsPkDse1Nxjs1B6ACMKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T1NcU/btrC2CA9X1e/gIUsPkDse1Nxjs1B6ACMKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T1NcU/btrC2CA9X1e/gIUsPkDse1Nxjs1B6ACMKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT1NcU%2FbtrC2CA9X1e%2FgIUsPkDse1Nxjs1B6ACMKk%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;1185&quot; height=&quot;286&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 17136 java</category>
      <category>백준 색종이붙이기</category>
      <category>백트래킹</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/151</guid>
      <comments>https://katastrophe.tistory.com/151#entry151comment</comments>
      <pubDate>Wed, 25 May 2022 01:53:31 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] JPA 개요 및 영속성 컨텍스트</title>
      <link>https://katastrophe.tistory.com/150</link>
      <description>&lt;h1&gt;ORM 개요&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;OOP 시대에 객체를 RDBMS에 관리하다 보니 SQL 중심적인 개발을 하게 됨으로써 나타나는 문제점들을 해결하기 위해 도입.&lt;/p&gt;
&lt;p&gt;객체는 ↔ RDBMS 사이를 매끄럽게 연결하여 &lt;strong&gt;SQL 의존성을 덜어내고 OOP 중점적으로 개발&lt;/strong&gt;을 하는 것이 목표.&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;JPA (Java Persistense Api)&lt;/h1&gt;
&lt;p&gt;현재 Java 진형의 ORM 표준으로 인터페이스이고 구현체는 Hibernate, EclipseLink, DataNucleus 등 &lt;/p&gt;
&lt;p&gt;그 중 &lt;code&gt;Hibernate&lt;/code&gt; 가 우리가 흔히 쓰는 구현체이다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;SpringDataJPA&lt;/h1&gt;
&lt;p&gt;JPA를 한 단계 더 추상화 시켜 개발을 편하게 해주는 Spring에서 제공하는 인터페이스&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654015-cedadf4f-c5b7-4f2e-a545-ddfb9f1fe923.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;JPA 구동방식&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654027-b75150ea-3e57-4dce-b5d6-19cb4f58ff9f.png&quot; alt=&quot;Untitled 1&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;persistence.xml에 jdbc 관련 정보 ( driver, username, password, url ) 등을 읽어와서&lt;br&gt;&lt;code&gt;EntityManagerFactory&lt;/code&gt;에 저장하고 &lt;code&gt;EntityManager&lt;/code&gt; 를 통해 작업을 한다.&lt;/p&gt;
&lt;p&gt;EntityManagerFactory 는 하나만 생성해서 어플리케이션 전체에 공유되고&lt;br&gt;Threadsafe 한 EntityManager를 생성**하고 내부엔 &lt;code&gt;영속성 컨텍스트&lt;/code&gt;가 있음.&lt;br&gt;한번 사용한 EntityManager 는 사용 후 버려지는데 사용 단위는 &lt;code&gt;Transaction&lt;/code&gt; 범위&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;영속성 컨텍스트&lt;/h1&gt;
&lt;p&gt;엔티티를 영구적으로 저장하는 공간이며 내부에 &lt;code&gt;1차캐시&lt;/code&gt;가 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654048-a5c0fd4e-5121-44cd-b9b6-727e9737d0d8.png&quot; alt=&quot;Untitled 2&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DB에서 데이터를 조회할 땐 무조건 1차캐시를 먼저 확인하고 없으면 1차캐시로 데이터를 영속화를 한 후에 값을 반환한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Entity의 생명주기&lt;/h2&gt;
&lt;p&gt;Entity의 상태는 &lt;code&gt;비영속&lt;/code&gt;, &lt;code&gt;영속&lt;/code&gt;, &lt;code&gt;준영속&lt;/code&gt;, &lt;code&gt;삭제&lt;/code&gt;총 4가지로 나타낼 수 있음.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654104-d3abbc01-c6b5-418a-b11c-ac9e2c44822a.png&quot; alt=&quot;Untitled 4&quot;&gt;&lt;/p&gt;
&lt;h3&gt;비영속 (new)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;단순히 엔티티를 새로 생성한 상태&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Member member = new Member(&amp;quot;kim&amp;quot;); &lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;영속 (Managed)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;새로 생성한 엔티티가 영속성 컨텍스트에 의해 관리되는 상태&lt;br&gt;저장 or 조회를 실행하면 영속성컨텍스트가 먼저 엔티티를 관리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;EntityManager.persist(member); // 저장
EntityManager.find(id); //조회&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;준영속 (detach)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;영속성 컨텍스트가 엔티티를 관리하지 않는 상태&lt;br&gt;Query가 commit을 호출해도 영속성을 거치지 않는 상태이기 때문에 동작하지 않음&lt;br&gt;1차캐시에 남아있지 않으므로 당연히 DirtyChecking 기능도 동작하지 않음&lt;br&gt;영속성컨텍스트의 모든 기능을 이용할 수 없는 상태이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;EntityManager.detach(member); // 준영속으로 만들기
EntityManager.clear() // 영속성 컨텍스트 초기화&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;삭제 (removed)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;엔티티를 삭제한 상태&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;EntityManager.remove(member); // 삭제 명령&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;영속성 컨텍스트 Flush&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;엔티티의 생명주기는 위와 같으며, &lt;code&gt;Transaction.commit()&lt;/code&gt; 과 같이 커밋이 호출되어야 DB에 반영이 된다. 이를 &lt;code&gt;Flush&lt;/code&gt; 라고 한다.&lt;br&gt;그 전까진 영속성컨텍스트가 붙잡고 있는 상태이다. ( Flush가 되어도 영속성 컨텍스트가 사라지는건 x )&lt;/p&gt;
&lt;p&gt;그럼 커밋이 호출되는 시점, 즉 DB에 반영되는 시점 기준은 언제일까&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;EntityManager.flush()  를 직접호출&lt;/li&gt;
&lt;li&gt;Transaction commit (자동호출)&lt;/li&gt;
&lt;li&gt;JPQL 쿼리를 직접 날림 (자동호출)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 3가지 조건일 때 DB에 작업한 내용이 반영이 된다. ( 반영이 되어도 영속성 컨텍스트엔 내용이 남아 있음)&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;영속성 컨텍스트의 이점&lt;/h1&gt;
&lt;p&gt;위와 같은 내용을 바탕으로 영속성컨텍스트에 의해 관리를 받을때 얻을 수 있는 이점이 있다.&lt;/p&gt;
&lt;h2&gt;1. Entity 동일성 (==) 보장&lt;/h2&gt;
&lt;p&gt;데이터 값이 같은 두 객체는 참조하는 주소 값이 다르다.&lt;/p&gt;
&lt;p&gt;하지만 EntityManager에 의해 관리되는 상태이면 두 객체는 같은 참조를 지닌다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Slf4j
@SpringBootTest
@Transactional
class UserTest {

    @Autowired
    EntityManager entityManager;

    @DisplayName(&amp;quot;userA id 값으로 조회&amp;quot;)
    @Test
    void same(){
        User userA = new User(&amp;quot;userA&amp;quot;);
        entityManager.persist(userA);

        User findUser = entityManager.find(User.class, userA.getId());

        log.info(&amp;quot;user A = {}&amp;quot;, userA);
        log.info(&amp;quot;findUser = {}&amp;quot;,findUser);

        Assertions.assertSame(userA,findUser);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654163-6c5dcbac-2c4b-4744-9af5-6fce267b7be5.png&quot; alt=&quot;Untitled 5&quot;&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 쓰기지연&lt;/h2&gt;
&lt;p&gt;모든 데이터 변경 Query를 쓰기지연 저장소에 보관해 놨다가 commit을 수행할 때 한번에 날린다. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654127-7e83695d-9866-425c-aeb7-880f9149d218.png&quot; alt=&quot;Untitled 6&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Slf4j
@SpringBootTest
@Transactional
@Rollback(false)
class UserTest {

    @Autowired
    EntityManager entityManager;

    @Test
    void lazy(){

        log.info(&amp;quot;===== persist 효출 전 =====&amp;quot;);
        User userA = new User(&amp;quot;userA&amp;quot;);
        entityManager.persist(userA);
        log.info(&amp;quot;===== persist 효출 후 =====&amp;quot;);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654197-4e16d73e-8cfb-4ef0-be72-6d7d83c098b7.png&quot; alt=&quot;Untitled 7&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;persist 를 호출해도 바로 insert query가 발생하지 않고 commit시점에 호출이 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. DirtyChecking&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Slf4j
@SpringBootTest
@Transactional
@Rollback(false)
class UserTest {

    @Autowired
    EntityManager entityManager;

    @Test
    void dirtyChecking(){

        User userA = new User(&amp;quot;userA&amp;quot;);
        entityManager.persist(userA);

        userA.setName(&amp;quot;userB&amp;quot;);

        entityManager.flush();
        entityManager.clear();

        User userB = entityManager.find(User.class, userA.getId());
        log.info(&amp;quot;변경 후 = {}&amp;quot;,userB);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654236-a0411a49-50be-4d35-896c-0beda182676a.png&quot; alt=&quot;Untitled 8&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654267-d182de10-445d-4e65-b947-fc6a1bbedc55.png&quot; alt=&quot;Untitled 9&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;모든 Entity는 영속성 컨텍스트에서 관리되며 데이터의 CRUD를 추적.&lt;/p&gt;
&lt;p&gt;변경이 일어나면 영속성 컨텍스트에서 &lt;code&gt;DirtyChecking&lt;/code&gt;을 통해 1차캐시에서 먼저 조회를 한 후 스냅샷과 값이 다르면 &lt;strong&gt;commit&lt;/strong&gt; 시점에 이를 반영한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. 지연로딩&lt;/h2&gt;
&lt;p&gt;만약 연관관계에 있는 두 Entity 중(ex. user, team) 하나(user)를 호출 하였을때&lt;br&gt;그 와 연관된 다른 엔티티는 실제로 호출되기 전 까진 호출되지 않는다.&lt;/p&gt;
&lt;h3&gt;User만 호출 했을 경우 (user에 대한 select query 한번만 나감)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Slf4j
@SpringBootTest
@Rollback(value = false)
@Transactional
class UserTest {

    @Autowired
    EntityManager entityManager;

    Long userId;

    @BeforeEach
    void save(){

        User userA = new User(&amp;quot;userA&amp;quot;);
        Team team = new Team(&amp;quot;team&amp;quot;);

        team.users.add(userA);

        userA.setTeam(team);

        entityManager.persist(team);
        userId = userA.getId();

        // 영속성 컨텍스트 초기화
        entityManager.flush();
        entityManager.clear();
    }

    @Test
    @DisplayName(&amp;quot;team is null&amp;quot;)
    void test(){

        User user = entityManager.find(User.class, userId);
        Team team = user.getTeam();

        Assertions.assertNull(team.name);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654285-2657bc2e-34b8-4b54-804e-afa38f909fe4.png&quot; alt=&quot;Untitled 10&quot;&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;User의 Team 까지 호출 하였을 경우 (프록시 강제초기화)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Test
    @DisplayName(&amp;quot;team is not null&amp;quot;)
    void test(){

        User user = entityManager.find(User.class, userId);
        String teamName = user.getTeam().getName();

        Assertions.assertNotNull(teamName);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45655434/169654296-25a7f6df-f898-48fc-80f1-f98b402b7d96.png&quot; alt=&quot;Untitled 11&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;team 의 getName() 을 호출해야만 team에 대한 select query가 한번 더 나간다.&lt;br&gt;즉, 실제로 사용하기 전 까진 query가 발생하지 않아 리소스 절감에 도움이 된다.&lt;/p&gt;
&lt;p&gt;이 옵션을 사용하기 위해선 연관관계를 지정할때 &lt;code&gt;FetchType=Lazy&lt;/code&gt;로 설정해야 동작함.&lt;br&gt;(Default 옵션으로 &lt;code&gt;Eager&lt;/code&gt; 이 설정되어 있어 query가 user만 호출해도 2번 발생함)&lt;br&gt;보통 &lt;code&gt;@X To One&lt;/code&gt; 관계일때 Lazy로 걸어둔다. ( 최적화 관련 옵션 )&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;연관관계&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;DB의 한쪽 엔티티가 다른 엔티티와의 연관된 데이터를 이용하기 위해선 한쪽에서 외래키를 가지고 있어야 한다.&lt;/p&gt;
&lt;p&gt;JPA에서는 필드에 외래키로 관리하는 대신 엔티티를 필드에 추가하고 연관관계의 주인을 지정해준다. ( FK 를 가지고 있는 쪽이 연관관계의 주인이다.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Entity
@Setter @Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {

    @Id
    @GeneratedValue
    Long id;
    String name;

    @JoinColumn(name = &amp;quot;team_id&amp;quot;)
    @ManyToOne(fetch = FetchType.LAZY)
    Team team;

    public User(String name) {
        this.name = name;
    }

}

@Entity
@Setter @Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {

    @Id @GeneratedValue
    Long id;
    String name;

    @OneToMany(mappedBy = &amp;quot;team&amp;quot;,cascade = CascadeType.ALL) 
    List&amp;lt;User&amp;gt; users = new ArrayList&amp;lt;&amp;gt;();

    public Team(String name) {
        this.name = name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;연관관계의 종류&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;N:M (@ManyToMany) ⇒ 필드추가가 불가능하므로 일대다 다대일로 풀어쓴다.&lt;/li&gt;
&lt;li&gt;1:N (@OneToMany) ⇒ mappedby 옵션으로 연관관계의 주인을 지정&lt;/li&gt;
&lt;li&gt;N:1 (@ManyToOne) ⇒ 외래키 지정&lt;/li&gt;
&lt;li&gt;1:1 (@OneToOne) ⇒ 외래키 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;출처 : 자바 ORM 표준 JPA 프로그래밍&lt;/h3&gt;</description>
      <category>Backend/JPA</category>
      <category>JPA개요</category>
      <category>JPA란</category>
      <category>영속성컨텍스트</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/150</guid>
      <comments>https://katastrophe.tistory.com/150#entry150comment</comments>
      <pubDate>Tue, 24 May 2022 02:40:56 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 폼 유효성(validation) 검사하기</title>
      <link>https://katastrophe.tistory.com/149</link>
      <description>&lt;h1&gt;폼 입력 유효성 검사하기&lt;/h1&gt;
&lt;h1&gt;Validation 개요&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;사용자가 만약 특정 로직을 수행 했는데 잘못된 입력으로 인해 어플리케이션이 오작동을 하면 안된다. 그러므로 &lt;strong&gt;검증&lt;/strong&gt;을 통해 처리해 줘야 함.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Validation 사용&lt;/h1&gt;
&lt;p&gt;검증을 수행하기 위해선 다음과 같은 의존성 모듈이 필요&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;org.springframework.boot:spring-boot-starter-validation&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;검증을 위한 라이브러리는 두 가지가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;javax&lt;/code&gt; 표준 스펙(jsr-303) Bean Validator : &lt;a href=&quot;https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/package-summary.html&quot;&gt;공식문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hibernate&lt;/code&gt; validator : &lt;a href=&quot;https://docs.jboss.org/hibernate/validator/6.2/reference/en-US/html_single/#section-builtin-constraints&quot;&gt;공식문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@Valid&lt;/code&gt; 는 annotation 이 javax 표준 검증기이고 컨트롤러 메소드 유효성 검사만 가능.&lt;br&gt;&lt;code&gt;@Validated&lt;/code&gt; 는 Spring 에서 지원해주는 검증기이고 다른 계증에서도 사용이 가능. (AOP기반)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;LocalValidatorFactoryBean&lt;/code&gt; 이 annotation을 보고 검증을 수행.&lt;br&gt;실무에선 대부분 hibernate 의 validator를 사용한다고 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;검사 규칙 선언하기 예시&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;package springinaction.tacos.entity;

import lombok.Data;
import org.hibernate.validator.constraints.CreditCardNumber;

import javax.validation.constraints.Digits;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

@Data
public class Order {

    @NotBlank(message = &amp;quot;Name is required&amp;quot;)
    private String deliveryName;

    @NotBlank(message = &amp;quot;Street is required&amp;quot;)
    private String deliveryStreet;

    @NotBlank(message = &amp;quot;City is required&amp;quot;)
    private String deliveryCity;

    @NotBlank(message = &amp;quot;State is required&amp;quot;)
    private String deliveryState;

    @NotBlank(message = &amp;quot;Zip code is required&amp;quot;)
    private String deliveryZip;

    @CreditCardNumber(message = &amp;quot;Not a valid credit card number&amp;quot;)
    private String ccNumber;

    @Pattern(regexp = &amp;quot;^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$&amp;quot;,
            message = &amp;quot;Must be formatted MM/YY&amp;quot;)
    private String ccExpiration;

    @Digits(integer = 3, fraction = 0, message = &amp;quot;Invalid CVV&amp;quot;)
    private String ccCVV;
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;package springinaction.tacos.entity;

import lombok.Data;
import net.bytebuddy.implementation.bind.annotation.Empty;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.List;

@Data
public class Taco {

    @NotBlank
    @Size(min=5,message = &amp;quot;최소 5글자 필요&amp;quot;)
    private String name;

    @Size(min=1, message = &amp;quot;최소 하나이상의 재료필요&amp;quot;)
    private List&amp;lt;String&amp;gt; ingredients;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;@CreditCardNumber&lt;/code&gt; 와 같은 검증은 hibernate 의 validator가 수행한다는 것을 알 수 있다.&lt;/p&gt;
&lt;h1&gt;폼과 바인딩 그리고 검증&lt;/h1&gt;
&lt;p&gt;사용자가 입력한 form 을 통해 서버로 넘어왔다고 가정하면 &lt;code&gt;@Valid&lt;/code&gt; annotation 이 붙어 있는 필드를 검증하게 된다. 그리고 검증이 실패할 시 &lt;code&gt;Errors&lt;/code&gt; 에 담긴다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@PostMapping
    public String processDesign(@Valid Taco design, Errors errors) {
        if (errors.hasErrors()) {
            List&amp;lt;FieldError&amp;gt; list = errors.getFieldErrors();
            list.forEach(e-&amp;gt;log.error(e.getDefaultMessage()));

            return &amp;quot;design&amp;quot;;
        }

        log.info(&amp;quot;Processing design: &amp;quot; + design);
        return &amp;quot;redirect:/orders/current&amp;quot;;
    }&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;참고) 검증 결과를 담은 &lt;code&gt;BindingResult&lt;/code&gt;와 &lt;code&gt;fieldError&lt;/code&gt; &lt;code&gt;grobalError&lt;/code&gt; 등이 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fieldError&lt;/code&gt;는 &lt;code&gt;messageConverter&lt;/code&gt; 작동이 성공해서 정상적으로 바인딩을 하고 나서 필드 유효성을 검증한 뒤에 실패하면 &lt;code&gt;BingdingResult&lt;/code&gt;에 담겨지고&lt;br&gt;&lt;code&gt;grobalError&lt;/code&gt;는 비즈니스로직 관련해서 발생한 오류를 &lt;code&gt;BingdingResult&lt;/code&gt;에 담는다.&lt;br&gt;( BindingResult는 Errors를 상속받은 인터페이스임 )&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;만약 바인딩 자체에 실패한다면 ..? 컨트롤러가 호출되지 않음 .. !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;만약 검증에 실패시 &lt;code&gt;bindingResult.hasErrors()&lt;/code&gt; 를 수행해서 예외 처리를 하는 방식으로 한다.&lt;br&gt;추후 뒷장에 더 상세하게 나와있으니 pass&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;에러 메세지 출력해보기&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;2022-05-15 02:27:10.705  INFO 11824 --- [nio-8080-exec-7] s.t.web.controller.DesignTacoController  : Processing design: Taco(name=12345, ingredients=[FLTO, GRBF, CHED, TMTO, SLSA])
2022-05-15 02:27:18.455 ERROR 11824 --- [nio-8080-exec-1] s.tacos.web.controller.OrderController   : Must be formatted MM/YY
2022-05-15 02:27:18.456 ERROR 11824 --- [nio-8080-exec-1] s.tacos.web.controller.OrderController   : Not a valid credit card number&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;만약 검증에 실패한다면&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;사용자가 입력한 form 검증이 실패하면 다시 입력할 수 있도록 입력폼을 불러와야 하는데 이전의 입력한 값들을 유지하고 싶다면 &lt;code&gt;Thymeleaf&lt;/code&gt; 에서 다음과 같이 수행할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;
      xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;EUC-KR&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Taco Cloud&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; th:href=&amp;quot;@{/css/styles.css}&amp;quot;/&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;form method=&amp;quot;POST&amp;quot; th:action=&amp;quot;@{/orders}&amp;quot; th:object=&amp;quot;${order}&amp;quot;&amp;gt;
    &amp;lt;h1&amp;gt;Order your taco creations!&amp;lt;/h1&amp;gt;

    &amp;lt;img th:src=&amp;quot;@{/images/TacoCloud.png}&amp;quot;/&amp;gt; &amp;lt;a th:href=&amp;quot;@{/design}&amp;quot; id=&amp;quot;another&amp;quot;&amp;gt;Design another taco&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;

    &amp;lt;div th:if=&amp;quot;${#fields.hasErrors()}&amp;quot;&amp;gt;
            &amp;lt;span class=&amp;quot;validationError&amp;quot;&amp;gt; Please correct the problems below and resubmit. &amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;h3&amp;gt;Deliver my taco masterpieces to...&amp;lt;/h3&amp;gt;

    &amp;lt;label for=&amp;quot;deliveryName&amp;quot;&amp;gt;Name: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;deliveryName&amp;quot; th:field=&amp;quot;*{deliveryName}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;deliveryName&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{deliveryName}&amp;quot;&amp;gt;Name Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;label for=&amp;quot;deliveryStreet&amp;quot;&amp;gt;Street address: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;deliveryStreet&amp;quot; th:field=&amp;quot;*{deliveryStreet}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;deliveryStreet&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{deliveryStreet}&amp;quot;&amp;gt;Street Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;label for=&amp;quot;deliveryCity&amp;quot;&amp;gt;City: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;deliveryCity&amp;quot; th:field=&amp;quot;*{deliveryCity}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;deliveryCity&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{deliveryCity}&amp;quot;&amp;gt;City Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;label for=&amp;quot;deliveryState&amp;quot;&amp;gt;State: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id =&amp;quot;deliveryState&amp;quot; th:field=&amp;quot;*{deliveryState}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;deliveryState&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{deliveryState}&amp;quot;&amp;gt;State Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;label for=&amp;quot;deliveryZip&amp;quot;&amp;gt;Zip code: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id =&amp;quot;deliveryZip&amp;quot; th:field=&amp;quot;*{deliveryZip}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;deliveryZip&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{deliveryZip}&amp;quot;&amp;gt;Zip Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;h3&amp;gt;Here&amp;#39;s how I&amp;#39;ll pay...&amp;lt;/h3&amp;gt;
    &amp;lt;label for=&amp;quot;ccNumber&amp;quot;&amp;gt;Credit Card #: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id =&amp;quot;ccNumber&amp;quot; th:field=&amp;quot;*{ccNumber}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;ccNumber&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{ccNumber}&amp;quot;&amp;gt;CC Num Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;label for=&amp;quot;ccExpiration&amp;quot;&amp;gt;Expiration: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id =&amp;quot;ccExpiration&amp;quot; th:field=&amp;quot;*{ccExpiration}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;ccExpiration&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{ccExpiration}&amp;quot;&amp;gt;CC Num Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;label for=&amp;quot;ccCVV&amp;quot;&amp;gt;CVV: &amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id =&amp;quot;ccCVV&amp;quot; th:field=&amp;quot;*{ccCVV}&amp;quot;/&amp;gt;
    &amp;lt;span class=&amp;quot;validationError&amp;quot;
          th:if=&amp;quot;${#fields.hasErrors(&amp;#39;ccCVV&amp;#39;)}&amp;quot;
          th:errors=&amp;quot;*{ccCVV}&amp;quot;&amp;gt;CC Num Error&amp;lt;/span&amp;gt;
    &amp;lt;br/&amp;gt;

    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Submit order&amp;quot;/&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;tip) input 태그에 &lt;code&gt;id&lt;/code&gt; 값을 추가로 넣어주면 해당 input 태그의 id 식별이 가능해서 추적이 용이함.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;th:field = *{필드명}&lt;/code&gt; 을 통해 field 를 식별하고&lt;br&gt;&lt;code&gt;th:if=&amp;quot;${#fields.hasErrors(&amp;#39;필드명&amp;#39;)}”&lt;/code&gt; 을 통해 만약 그 field 에서 오류가 발생하면&lt;br&gt;&lt;code&gt;th:errors=&amp;quot;*{필드명}&amp;quot;&lt;/code&gt; 과 같이 에러가 발생한 필드를 재 출력한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 외 만약 에러가 발생했을 경우 별도로 css class를 적용할 수 있는&lt;br&gt;&lt;code&gt;th : errorclass&lt;/code&gt; 를 타임리프가 지원해준다.&lt;/p&gt;
&lt;h3&gt;출처 : 스프링 인 액션&lt;/h3&gt;</description>
      <category>Backend/Spring</category>
      <category>Form Validation</category>
      <category>백엔드</category>
      <category>스프링 검증</category>
      <category>타임리프</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/149</guid>
      <comments>https://katastrophe.tistory.com/149#entry149comment</comments>
      <pubDate>Tue, 24 May 2022 02:35:17 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [15787] 기차가 어둠을 헤치고 은하수를 JAVA</title>
      <link>https://katastrophe.tistory.com/148</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15787&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/15787&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652882972910&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;15787번: 기차가 어둠을 헤치고 은하수를&quot; data-og-description=&quot;입력의 첫째 줄에 기차의 수 N(1 &amp;le; N &amp;le; 100000)과 명령의 수 M(1 &amp;le; M &amp;le; 100000)가 주어진다. 이후 두 번째 줄부터 M+1번째 줄까지 각 줄에 명령이 주어진다.&amp;nbsp;&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15787&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15787&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/r4Bwb/hyOstkRHqP/0PbL2Dg4wQxWHb2LFUlqd0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/bWTZDV/hyOsqIqve8/bvezhQB9JRW7USG5QeAYZK/img.png?width=785&amp;amp;height=427&amp;amp;face=0_0_785_427&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15787&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15787&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/r4Bwb/hyOstkRHqP/0PbL2Dg4wQxWHb2LFUlqd0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/bWTZDV/hyOsqIqve8/bvezhQB9JRW7USG5QeAYZK/img.png?width=785&amp;amp;height=427&amp;amp;face=0_0_785_427');&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;15787번: 기차가 어둠을 헤치고 은하수를&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;입력의 첫째 줄에 기차의 수 N(1 &amp;le; N &amp;le; 100000)과 명령의 수 M(1 &amp;le; M &amp;le; 100000)가 주어진다. 이후 두 번째 줄부터 M+1번째 줄까지 각 줄에 명령이 주어진다.&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wKqzC/btrCw7gW2KY/gJfBEOpFO0VJYT7BmeQOp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wKqzC/btrCw7gW2KY/gJfBEOpFO0VJYT7BmeQOp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wKqzC/btrCw7gW2KY/gJfBEOpFO0VJYT7BmeQOp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwKqzC%2FbtrCw7gW2KY%2FgJfBEOpFO0VJYT7BmeQOp0%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;959&quot; height=&quot;709&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;709&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;933&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2hq0R/btrCsdwgqPj/LxOJmEVi6jhIEkW6PX2RFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2hq0R/btrCsdwgqPj/LxOJmEVi6jhIEkW6PX2RFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2hq0R/btrCsdwgqPj/LxOJmEVi6jhIEkW6PX2RFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2hq0R%2FbtrCsdwgqPj%2FLxOJmEVi6jhIEkW6PX2RFK%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;933&quot; height=&quot;661&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;주어진 수의 범위가 커서 비트 마스킹으로 풀어야 하는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;0000...00000 으로 총 21개의 0으로 기차의 상태를 체크한다. ( &amp;lt;&amp;lt; 연산을 적용하면 10 부터 시작하기 때문)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;명령의 경우&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 1 =&amp;gt; 해당 위치에 &amp;lt;&amp;lt; 연산을 적용시킨 결과와 or 연산 적용&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 2 =&amp;gt; 1번에 not 을 적용시킨 결과와 and 연산 적용&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 3 =&amp;gt; &amp;lt;&amp;lt; 연산으로 한칸 밀어내고 21개의 1 과 and 연산으로 사이즈 조절&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 4 =&amp;gt; &amp;gt;&amp;gt; 연산으로 한칸 밀어내고 not 연산을 적용시킨 ( 11111110) 과 and 연산&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;나올 수 있는 모든 경우를 카운트한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고) 비트마스크 연산&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/esgVXP/btrCyefPHj9/0K6ihx9HCMXsumdO08YKkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/esgVXP/btrCyefPHj9/0K6ihx9HCMXsumdO08YKkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/esgVXP/btrCyefPHj9/0K6ihx9HCMXsumdO08YKkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FesgVXP%2FbtrCyefPHj9%2F0K6ihx9HCMXsumdO08YKkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;671&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;pre id=&quot;code_1652887093381&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Arrays.stream;


public class Main {

    static int n, m;
    static int[] trains;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt)
                .toArray();
        n = input[0];
        m = input[1];
        trains = new int[n + 1];

        for (int i = 0; i &amp;lt; m; i++) { // command

            input = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt)
                    .toArray();

            int command = input[0];
            int index = input[1];
            int seat = input.length == 2 ? 0 : input[2];

            switch (command) {
                case 1 -&amp;gt; trains[index] |= (1 &amp;lt;&amp;lt; seat);
                case 2 -&amp;gt; trains[index] &amp;amp;= ~(1 &amp;lt;&amp;lt; seat);
                case 3 -&amp;gt; {
                    trains[index] &amp;lt;&amp;lt;= 1;
                    trains[index] &amp;amp;= ((1 &amp;lt;&amp;lt; 21) - 1);
                }
                case 4 -&amp;gt; {
                    trains[index] &amp;gt;&amp;gt;= 1;
                    trains[index] &amp;amp;= ~1;
                }
            }
        }

        int ans = (int) stream(trains, 1, n + 1)
                .distinct()
                .count();

        System.out.println(ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chDHc9/btrCwgloxQz/loxkslJKccXZ5xSDz2Lo4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chDHc9/btrCwgloxQz/loxkslJKccXZ5xSDz2Lo4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chDHc9/btrCwgloxQz/loxkslJKccXZ5xSDz2Lo4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchDHc9%2FbtrCwgloxQz%2FloxkslJKccXZ5xSDz2Lo4K%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;937&quot; height=&quot;235&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>기차가 어둠을 헤치고 은하수를 java</category>
      <category>백준 15787 java</category>
      <category>비트마스킹</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/148</guid>
      <comments>https://katastrophe.tistory.com/148#entry148comment</comments>
      <pubDate>Thu, 19 May 2022 00:17:39 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1052] 물병JAVA</title>
      <link>https://katastrophe.tistory.com/147</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1052&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1052&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652877236634&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;1052번: 물병&quot; data-og-description=&quot;지민이는 N개의 물병을 가지고 있다. 각 물병에는 물을 무한대로 부을 수 있다. 처음에 모든 물병에는 물이 1리터씩 들어있다. 지민이는 이 물병을 또 다른 장소로 옮기려고 한다. 지민이는 한 번&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1052&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1052&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AwFMF/hyOsrtI97z/XPdtkl2cC3IaPxUDc5sqFK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1052&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1052&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AwFMF/hyOsrtI97z/XPdtkl2cC3IaPxUDc5sqFK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1052번: 물병&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;지민이는 N개의 물병을 가지고 있다. 각 물병에는 물을 무한대로 부을 수 있다. 처음에 모든 물병에는 물이 1리터씩 들어있다. 지민이는 이 물병을 또 다른 장소로 옮기려고 한다. 지민이는 한 번&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EzXXR/btrCxI2fuDi/1ns5tY2CdP8433DG1cRQzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EzXXR/btrCxI2fuDi/1ns5tY2CdP8433DG1cRQzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EzXXR/btrCxI2fuDi/1ns5tY2CdP8433DG1cRQzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEzXXR%2FbtrCxI2fuDi%2F1ns5tY2CdP8433DG1cRQzk%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;1002&quot; height=&quot;780&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;780&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;963&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rBcYT/btrCwGKNXrJ/9YJGCYspkkhjmM6d0gKLi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rBcYT/btrCwGKNXrJ/9YJGCYspkkhjmM6d0gKLi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rBcYT/btrCwGKNXrJ/9YJGCYspkkhjmM6d0gKLi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrBcYT%2FbtrCwGKNXrJ%2F9YJGCYspkkhjmM6d0gKLi0%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;963&quot; height=&quot;386&quot; data-origin-width=&quot;963&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;경우의 수 를 하나씩 따져보면 -1 을 출력할 일은 없다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;우선 물통을 안사고 최대한 합친다고 해보자&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;주어진 n 이 다음과 같을 때 하나씩 보자.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 1 : 0&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 2 : 1&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 3 : 2 1&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 4 : 4&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 5 : 4 1&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;case 6 : 4 2&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;...&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;u&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;남는 물병이 점점 늘어난다. =&amp;gt; 병이 모자랄 일은 x&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;&lt;br /&gt;&lt;u&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그리고 2^n 의 갯수이면 물병을 사지 않아도 된다.&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;ex) n=13 k=2 이면&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;안사고 최대한 합칠 수 있는 경우는 [8 4 1] 이다. k 보다 크다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;하지만 3병을 더 사면 [16] . 즉, 한 병에 모두 담아갈 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이제 k개의 물병에 나눠 가져간다고 해보자.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 주어진 물 n 이 k 보다 이하 라면 ? =&amp;gt; 그냥 다 들고 가면 됨&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 주어진 물 n 이 k 보다 작다면 ?? =&amp;gt; 물병들을 일부 합쳐서 가져가야 됨&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2번의 과정은 2^n 형태로 만드는 과정이다. 1번의 조건이 만족할 때 까지.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2^n 형태로 만들기 위해 필요한 추가 물병의 갯수를 구하면 된다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;n=13 : 물병의 갯수가 홀수이면 하나가 계속 남기때문에 1개를 추가구매한다. 그리고 2로 나눈다. =&amp;gt; 7&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;n=7 : 물병의 개수가 홀수이므로 1개를 추가 구매하고 2로 나눈다. =&amp;gt; 3&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;n=3 : 2&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;즉 , 3개를 더 사면 n=13 k=2를 만족한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;if( n%2 == 1) :&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; n = n/2&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; cnt++&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; n++&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이런식으로 진행하지만 비트마스킹을 이용해 풀 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;u&gt;n을 2진수로 변환했을 때 1의 갯수가 k 보다 클 동안 반복&lt;/u&gt; :&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;u&gt;1이 처음으로 나타나면 합칠 때 홀수가 발생하는 숫자&lt;/u&gt;이므로 그 위치의 수 만큼 cnt를 늘려준다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;cnt += n&amp;amp;(-n)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;n+=n&amp;amp;(-n)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1652882848304&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Arrays.stream;


public class Main {

    static int n, k;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt)
                .toArray();
        n = input[0];
        k = input[1];
        
        int ans = 0;

        while (Integer.bitCount(n) &amp;gt; k) {
            ans += n &amp;amp; (-n);
            n += n &amp;amp; (-n);
        }
        System.out.println(ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAZybx/btrCvYLZiRR/3SSVu2cDMnDsOVofESHBW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAZybx/btrCvYLZiRR/3SSVu2cDMnDsOVofESHBW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAZybx/btrCvYLZiRR/3SSVu2cDMnDsOVofESHBW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAZybx%2FbtrCvYLZiRR%2F3SSVu2cDMnDsOVofESHBW1%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;930&quot; height=&quot;217&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 1052 java</category>
      <category>백준 물병 java</category>
      <category>비트마스킹</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/147</guid>
      <comments>https://katastrophe.tistory.com/147#entry147comment</comments>
      <pubDate>Wed, 18 May 2022 23:08:00 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [17124] 두개의배열 JAVA</title>
      <link>https://katastrophe.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/17124&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652457988045&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;17124번: 두 개의 배열&quot; data-og-description=&quot;정수 배열 A 와 B가 있다. A는 총 n개의 서로 다른 양의 정수를 포함하고 B는 총 m개의 서로 다른 양의 정수를 포함한다. A, B를 이용해서 길이가 n인 새로운 배열 C를 만들어보자. C[i] 는 배열 B에 있&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/17124&quot; data-og-url=&quot;https://www.acmicpc.net/problem/17124&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/17124&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;17124번: 두 개의 배열&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;정수 배열 A 와 B가 있다. A는 총 n개의 서로 다른 양의 정수를 포함하고 B는 총 m개의 서로 다른 양의 정수를 포함한다. A, B를 이용해서 길이가 n인 새로운 배열 C를 만들어보자. C[i] 는 배열 B에 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQtb4j/btrB4E1UfD5/gQY06M8cEeSRLkzqfjGFMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQtb4j/btrB4E1UfD5/gQY06M8cEeSRLkzqfjGFMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQtb4j/btrB4E1UfD5/gQY06M8cEeSRLkzqfjGFMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQtb4j%2FbtrB4E1UfD5%2FgQY06M8cEeSRLkzqfjGFMk%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;1360&quot; height=&quot;836&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;836&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;1314&quot; data-origin-height=&quot;867&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB5AfB/btrB6POw4od/avsZAlZ076TN38AXuvEQHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB5AfB/btrB6POw4od/avsZAlZ076TN38AXuvEQHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB5AfB/btrB6POw4od/avsZAlZ076TN38AXuvEQHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB5AfB%2FbtrB6POw4od%2FavsZAlZ076TN38AXuvEQHk%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;1314&quot; height=&quot;867&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;867&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이분탐색 or 이진탐색 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;해당 위치를 기준으로 앞 뒤를 파악해야 하므로 lower higher 메서드를 제공해주는&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;TreeSet을 이용한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;B 배열이 기준이므로 B배열을 TreeSet으로 만든다음&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;A 배열 원소만큼 반복문을 돌린다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;찾은 결과 원소를 result 라고 하면&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. bSet 이 원소 e 를 가지고 있을 경우 =&amp;gt; result = e&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 가지고 있지 않을 경우&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; 2-1. bSet 에 e 를 추가해서 lower higher 를 호출&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; 2-2. lower = null =&amp;gt; 추가한 원소가 맨 앞 자리이므로 result = higher&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; higher = null =&amp;gt; 추가한 원소가 맨 뒷 자리이므로 result = lower&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 그 외 =&amp;gt; e-lower 과 e-higher 를 비교해서 가장 차이가 적은 것이 result&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1652458437365&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Arrays.stream;


public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int tc = Integer.parseInt(br.readLine());
        while (tc--&amp;gt;0){ br.readLine();

            long sum = 0L;

            ArrayList&amp;lt;Integer&amp;gt; listA = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt)
                    .boxed()
                    .collect(Collectors.toCollection(ArrayList::new));

            TreeSet&amp;lt;Integer&amp;gt; setB = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt)
                    .boxed()
                    .collect(Collectors.toCollection(TreeSet::new)); // 기준 Set

            for (Integer e : listA) {

                int result=e; // 기준 Set 에 이미 있는 요소이면 바로 출력

                if(!setB.contains(e)){ // 없는 요소이면 위치 찾기

                    // 추가한 요소의 앞 뒤 확인
                    setB.add(e);
                    Integer lower = setB.lower(e);
                    Integer higher = setB.higher(e);

                    if(lower == null) // 맨 앞자리일 경우
                        result = higher;
                    else if(higher==null)  // 맨 뒷자리일 경우
                        result = lower;
                    else result = Math.abs(e - lower) &amp;lt;= Math.abs(e - higher) ?
                                lower : higher; // lower-e-higher &amp;gt;&amp;gt; 이 형태로 있으므로 앞 뒤 차이 계산해서 결정

                    setB.remove(e); // 다시 제거
                }
                sum+=(long)result;
            }
            System.out.println(sum);
        }
    }
}

/**

 *
 */&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mFmTJ/btrB4K81OsB/cT00PjMdHpxCHr5JPHQUQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mFmTJ/btrB4K81OsB/cT00PjMdHpxCHr5JPHQUQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mFmTJ/btrB4K81OsB/cT00PjMdHpxCHr5JPHQUQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmFmTJ%2FbtrB4K81OsB%2FcT00PjMdHpxCHr5JPHQUQK%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;1315&quot; height=&quot;336&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 17124 java</category>
      <category>백준 두개의배열 java</category>
      <category>이분탐색</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/146</guid>
      <comments>https://katastrophe.tistory.com/146#entry146comment</comments>
      <pubDate>Sat, 14 May 2022 01:14:27 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [19236] 청소년 상어JAVA</title>
      <link>https://katastrophe.tistory.com/145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/19236&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/19236&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652273073140&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;19236번: 청소년 상어&quot; data-og-description=&quot;첫째 줄부터 4개의 줄에&amp;nbsp;각 칸의 들어있는 물고기의 정보가 1번 행부터 순서대로 주어진다. 물고기의 정보는 두 정수 ai, bi로 이루어져 있고, ai는 물고기의 번호, bi는 방향을 의미한다. 방향 bi는&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/19236&quot; data-og-url=&quot;https://www.acmicpc.net/problem/19236&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tdnhp/hyOmxauixU/IkvKwaGgZNEpDUcM1V6jZ1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/o2lV4/hyOmxBwIKG/k5n8u757JKDDJvx1X4ZvQ0/img.png?width=666&amp;amp;height=676&amp;amp;face=0_0_666_676,https://scrap.kakaocdn.net/dn/QxoWD/hyOmy8izkN/xMmHyU4drB6nR3sYhqZkl0/img.png?width=656&amp;amp;height=664&amp;amp;face=0_0_656_664&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/19236&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/19236&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tdnhp/hyOmxauixU/IkvKwaGgZNEpDUcM1V6jZ1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/o2lV4/hyOmxBwIKG/k5n8u757JKDDJvx1X4ZvQ0/img.png?width=666&amp;amp;height=676&amp;amp;face=0_0_666_676,https://scrap.kakaocdn.net/dn/QxoWD/hyOmy8izkN/xMmHyU4drB6nR3sYhqZkl0/img.png?width=656&amp;amp;height=664&amp;amp;face=0_0_656_664');&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;19236번: 청소년 상어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄부터 4개의 줄에&amp;nbsp;각 칸의 들어있는 물고기의 정보가 1번 행부터 순서대로 주어진다. 물고기의 정보는 두 정수 ai, bi로 이루어져 있고, ai는 물고기의 번호, bi는 방향을 의미한다. 방향 bi는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTG3ql/btrBSZ5iIfr/tfYDKHZrySmeWjz4L7KylK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTG3ql/btrBSZ5iIfr/tfYDKHZrySmeWjz4L7KylK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTG3ql/btrBSZ5iIfr/tfYDKHZrySmeWjz4L7KylK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTG3ql%2FbtrBSZ5iIfr%2FtfYDKHZrySmeWjz4L7KylK%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;1212&quot; height=&quot;793&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;793&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;1185&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/37Ehi/btrBR9t23VO/6L6T5wdhaNdl0WKzhuhwX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/37Ehi/btrBR9t23VO/6L6T5wdhaNdl0WKzhuhwX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/37Ehi/btrBR9t23VO/6L6T5wdhaNdl0WKzhuhwX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F37Ehi%2FbtrBR9t23VO%2F6L6T5wdhaNdl0WKzhuhwX0%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;1185&quot; height=&quot;812&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deFbYu/btrBUHCPM3y/NKAtb4T91KezCBXEkVGv5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deFbYu/btrBUHCPM3y/NKAtb4T91KezCBXEkVGv5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deFbYu/btrBUHCPM3y/NKAtb4T91KezCBXEkVGv5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeFbYu%2FbtrBUHCPM3y%2FNKAtb4T91KezCBXEkVGv5k%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;1181&quot; height=&quot;887&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;887&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;1198&quot; data-origin-height=&quot;919&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0m9Xb/btrBUG4YI87/bXp2R5mLeqnlu9tKrp4iTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0m9Xb/btrBUG4YI87/bXp2R5mLeqnlu9tKrp4iTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0m9Xb/btrBUG4YI87/bXp2R5mLeqnlu9tKrp4iTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0m9Xb%2FbtrBUG4YI87%2FbXp2R5mLeqnlu9tKrp4iTk%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;1198&quot; height=&quot;919&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;919&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;1254&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ol4Di/btrBS0C7BH6/hc0xiqxa941KR8iaqk72hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ol4Di/btrBS0C7BH6/hc0xiqxa941KR8iaqk72hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ol4Di/btrBS0C7BH6/hc0xiqxa941KR8iaqk72hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fol4Di%2FbtrBS0C7BH6%2Fhc0xiqxa941KR8iaqk72hK%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;1254&quot; height=&quot;784&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;DFS 완전 탐색 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;중요한건 배열을 미리 clone 시켜놓고 탐색을 진행하고나서 다시 백트래킹 할 때 복제해둔 배열로 원복하는 것.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;물고기가 움직일 때 : moveFish&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 해당위치에 물고기가 없는 경우 =&amp;gt; 단순 물고기만 이동&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 해당위치에 상어가 있는 경우 =&amp;gt; 이동 x&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 그 외 =&amp;gt; 두 물고기위 위치를 swap&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;상어를 기준으로 dfs 를 호출할 때 마다 moveFish 를 호출해주고&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;상어가 움직이면서 먹을 수 있는 물고기의 최대 max 값을 갱신시키면 된다.&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1652273458333&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

import static java.util.Arrays.stream;


public class Main {
    
    static Fish[] fish = new Fish[17];
    static int[][] map = new int[4][4];
    static int max = 0;
    static int[] dx = {0, -1, -1, 0, 1, 1, 1, 0, -1};
    static int[] dy = {0, 0, -1, -1, -1, 0, 1, 1, 1};
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i &amp;lt; 4; i++) {
            for (int j = 0; j &amp;lt; 4; j++) {
                int fishNum = sc.nextInt();
                int fishDir = sc.nextInt();
                map[i][j] = fishNum;
                fish[fishNum] = new Fish(i, j, fishDir, true);
            }
        }
        //첫번째 위치 먹고 start
        int fishNum = map[0][0];
        fish[fishNum].alive = false;
        map[0][0] = -1;
        dfs(0, 0, fish[fishNum].dir, fishNum);
        System.out.println(max);
    }

    //상어 위치 (x, y)
    public static void moveFish(int x, int y) {
        for (int i = 1; i &amp;lt;= 16; i++) {
            Fish moveFish = fish[i];
            
            //이미 먹은 물고기인 경우
            if (!moveFish.alive) 
                continue;

            //8방향 이동여부 확인
            for (int j = 0; j &amp;lt; 8; j++) {
                int nextDir = (moveFish.dir + j) &amp;gt;= 9 ? (moveFish.dir + j) % 8 : (moveFish.dir + j);
                int nextX = moveFish.x + dx[nextDir];
                int nextY = moveFish.y + dy[nextDir];

                if (!isRange(nextX,nextY)|| (nextX == x &amp;amp;&amp;amp; nextY == y)) // 해당위치에 상어가 있으면 continue
                    continue;

                if (map[nextX][nextY] != -1) { // 물고기가 살아있어 위치를 바꿀때
                    Fish otherFish = fish[map[nextX][nextY]];
                    map[moveFish.x][moveFish.y] = map[nextX][nextY];
                    map[nextX][nextY] = i;
                    otherFish.x = moveFish.x;
                    otherFish.y = moveFish.y;

                } else { // 이미 죽은 물고기라서 단순 위치만 이동시킬 때
                    map[moveFish.x][moveFish.y] = -1;
                    map[nextX][nextY] = i;
                }
                moveFish.x = nextX;
                moveFish.y = nextY;
                moveFish.dir = nextDir;
                break;
            }

        }
    }

    public static void dfs(int x, int y, int d, int sum) {
        //결과값 갱신
        if (max &amp;lt; sum) max = sum;

        //fish, map 상태 저장해둠
        Fish[] cloneFish = new Fish[17];
        for (int i = 1; i &amp;lt; 17; i++)
            cloneFish[i] = new Fish(fish[i].x, fish[i].y, fish[i].dir, fish[i].alive);
        int[][] cloneMap = new int[4][4];
        for (int i = 0; i &amp;lt; 4; i++) {
            cloneMap[i] = map[i].clone();
        }
        moveFish(x, y); // 물고기 이동,회전

        for (int i = 1; i &amp;lt; 4; i++) { // 상어 이동
            int nextX = x + dx[d] * i;
            int nextY = y + dy[d] * i;

            if (!isRange(nextX,nextY))
                continue;

            int fishNum = map[nextX][nextY];
            if (fishNum != -1) {
                //살아있는 물고기가 있는 경우
                fish[fishNum].alive = false;
                map[nextX][nextY] = -1;
                dfs(nextX, nextY, fish[fishNum].dir, sum + fishNum); // 탐색

                // 백트래킹
                fish[fishNum].alive = true;
                map[nextX][nextY] = fishNum;
            }
        }

        //fish, map 상태 복구
        fish = cloneFish.clone();
        for (int i = 0; i &amp;lt; 4; i++)
            map[i] = cloneMap[i].clone();
    }

    static boolean isRange(int x,int y){
        return x&amp;gt;=0&amp;amp;&amp;amp;y&amp;gt;=0&amp;amp;&amp;amp;x&amp;lt;4&amp;amp;&amp;amp;y&amp;lt;4;
    }

    public static class Fish {
        int x, y, dir;
        boolean alive;

        public Fish(int x, int y, int dir, boolean alive) {
            this.x = x;
            this.y = y;
            this.dir = dir;
            this.alive = alive;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7btA8/btrBRYsonQM/84lYajZD7pIorIq6jWQECk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7btA8/btrBRYsonQM/84lYajZD7pIorIq6jWQECk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7btA8/btrBRYsonQM/84lYajZD7pIorIq6jWQECk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7btA8%2FbtrBRYsonQM%2F84lYajZD7pIorIq6jWQECk%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;1171&quot; height=&quot;284&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 19236 java</category>
      <category>백준 청소년상어 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/145</guid>
      <comments>https://katastrophe.tistory.com/145#entry145comment</comments>
      <pubDate>Wed, 11 May 2022 21:51:18 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [10844] 쉬운계단수JAVA</title>
      <link>https://katastrophe.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10844&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/10844&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652202968447&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;10844번: 쉬운 계단 수&quot; data-og-description=&quot;첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/10844&quot; data-og-url=&quot;https://www.acmicpc.net/problem/10844&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cXntsR/hyOk2oCrMn/Z01kLkVontnfRst9RQnPg0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10844&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/10844&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cXntsR/hyOk2oCrMn/Z01kLkVontnfRst9RQnPg0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;10844번: 쉬운 계단 수&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GDmFk/btrBMEG8wY5/IX1zDZ7DKqoJvzRmN95VtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GDmFk/btrBMEG8wY5/IX1zDZ7DKqoJvzRmN95VtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GDmFk/btrBMEG8wY5/IX1zDZ7DKqoJvzRmN95VtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGDmFk%2FbtrBMEG8wY5%2FIX1zDZ7DKqoJvzRmN95VtK%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;1200&quot; height=&quot;892&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;DP 를 활용한 문제이다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우선 1~9 까지는 한자리 수 니깐 1 이다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 2번째 자리수는 다음과 같은 상황이 나온다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0 + ?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1 + ?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2 + ?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3 + ?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4 + ?&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;...&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;9 + ?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 ? 에 올 숫자는 이전 숫자보다 +1 또는 -1 이다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런데, 예외가 있다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 &lt;u&gt;9 가 나왔다면 다음은 무조건 8만 나와야 한다&lt;/u&gt;. ( 98 )&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 &lt;u&gt;0 이라면 이전은 다음건 1만 나와야 한다.&lt;/u&gt; 예를들어 10 ? 이 경우엔 다음수는 1만 가능 =&amp;gt; 101&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그럼 끝자리 경우 자체가 결정자가 될수 있다는 말이고 , 주어진 n 에 대해서도 결정자가 되어야 하니&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;점화식은 dp[자리수][0~9] 로 식을 세울 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;=&amp;gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt; dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1]&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0과 9일때 예외를 제외하고 bottom-up 방식 점화식&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1652203849817&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;
import static java.util.Arrays.stream;

public class Main {

    static int limit = 1000000000;
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[][] dp = new long[n+1][10];

        Arrays.fill(dp[1],1);
        dp[1][0]=0;

        for(int i=2; i&amp;lt;=n; i++){
            for(int j=0; j&amp;lt;10; j++){

                long next = switch (j){ // 숫자 + j
                    case 0-&amp;gt;dp[i-1][1]; // 0 일경우 다음은 1만 와야됨
                    case 9-&amp;gt;dp[i-1][8]; // 9 일경우 다음은 8만 와야됨
                    default -&amp;gt; dp[i-1][j-1]+dp[i-1][j+1] % limit;
                };
                dp[i][j] = next;
            }
        }
        long result = stream(dp[n])
                .reduce((sum, e) -&amp;gt; (sum + e) % limit)
                .getAsLong();
        System.out.println(result);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2cWma/btrBGzG2BWF/3OvhUPDSAKBpMA7RST0Px1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2cWma/btrBGzG2BWF/3OvhUPDSAKBpMA7RST0Px1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2cWma/btrBGzG2BWF/3OvhUPDSAKBpMA7RST0Px1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2cWma%2FbtrBGzG2BWF%2F3OvhUPDSAKBpMA7RST0Px1%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;1184&quot; height=&quot;280&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>다이나믹프로그래밍</category>
      <category>백준 10844 java</category>
      <category>백준 쉬운계단수 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/144</guid>
      <comments>https://katastrophe.tistory.com/144#entry144comment</comments>
      <pubDate>Wed, 11 May 2022 02:31:41 +0900</pubDate>
    </item>
    <item>
      <title>Spring 구조와 Component</title>
      <link>https://katastrophe.tistory.com/142</link>
      <description>&lt;h1&gt;스프링 구조&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;어플리케이션&lt;/strong&gt;은 기본적으로 &lt;code&gt;Components&lt;/code&gt; 간의 상호작용으로 동작하는데&lt;br&gt;&lt;code&gt;Component&lt;/code&gt; 또는 &lt;code&gt;Bean&lt;/code&gt; 형태로 스프링 컨테이너인 &lt;code&gt;ApplicationContext&lt;/code&gt; 라는 DI 컨테이너에 올려두고 관리 되는 구조.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;ApplicationContext 와 Bean 등록&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;빈을 등록하는 방법은 위와 같이 &lt;code&gt;annotation&lt;/code&gt; 기반으로 등록하거나 &lt;code&gt;xml&lt;/code&gt; 파일로 등록할 수 있다.&lt;br&gt;위와 같이 스프링 컨테이너에 빈을 등록하면 실제 어플리케이션이 동작할 때&lt;br&gt;의존성 주입 (DI) 을 기반으로 해서 스프링이 관리하는 빈으로 변경되어 수행된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Configuration // Coonfiguration 또한 Component 이다.
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserService(memberRepository());
    }

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;빈 이름&lt;/th&gt;
&lt;th&gt;빈 객체&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;userService&lt;/td&gt;
&lt;td&gt;UserService@x01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;userRepository&lt;/td&gt;
&lt;td&gt;UserRepository@x02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;등록한 bean 출력해보기&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;ApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);

AppConfig bean = ac.getBean(AppConfig.class);
System.out.println(&amp;quot;bean = &amp;quot; + bean.getClass());
//출력: bean = AppConfig$$EnhancerBySpringCGLIB$$bd479d70&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;실제로 &lt;code&gt;annotation&lt;/code&gt; 으로 등록한 &lt;code&gt;bean&lt;/code&gt; 을 출력해보면 &lt;code&gt;EnhancerBySpringCGLIB&lt;/code&gt; 라는 것이 출력이 된다. 즉, 스프링이 관리하는 bean 형태로 변환이 되어서 DI 컨테이너에 관리되고 있다는 것.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;@Autowired&lt;/code&gt; 를 필드, 메서드, 생성자에 붙여줌으로 써 필요한 &lt;code&gt;Component&lt;/code&gt; 를 주입받을 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;빈을 등록하는 예시&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;설정 정보  : &lt;code&gt;@Configuration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;빈 : &lt;code&gt;@Bean&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;컴포넌트 : &lt;code&gt;@Component&lt;/code&gt; ( &lt;code&gt;@Service&lt;/code&gt; &lt;code&gt;@Repository&lt;/code&gt; &lt;code&gt;@Controller&lt;/code&gt; 등 )&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같이 보통 &lt;code&gt;bean&lt;/code&gt;을 등록할 때 적절한 &lt;code&gt;annotation&lt;/code&gt;을 붙여서 등록을 한다.&lt;br&gt;&lt;code&gt;@Bean&lt;/code&gt; 형태로 등록하는 것은 &lt;code&gt;수동 bean&lt;/code&gt; 을 등록하는 방법이고&lt;br&gt;그 외는 &lt;code&gt;자동 bean&lt;/code&gt;을 등록하는 방법이다.&lt;/p&gt;
&lt;h3&gt;수동 bean 을 등록하는 기준&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;어플리케이션은 크게 &lt;strong&gt;업무 로직&lt;/strong&gt;과 &lt;strong&gt;기술 지원 로직&lt;/strong&gt;으로 나눌 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;업무로직&lt;/strong&gt;은 &lt;code&gt;Service&lt;/code&gt; &lt;code&gt;Repository&lt;/code&gt; &lt;code&gt;Controller&lt;/code&gt; 등 비즈니스 로직을 말한다. 만약 문제가 발생하면 어디서 발생했는지 명확히 파악하기가 쉽다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;기술지원 로직&lt;/strong&gt;은 기술적인 문제나 &lt;code&gt;AOP&lt;/code&gt;를 처리할 때 주로 사용된다. (log, DBconnetion 등 )&lt;br&gt;횡단 관심사 처리를 목적으로 어플리케이션 전반적으로 영향을 끼치기 때문에 만약 문제가 발생하면 어디서 발생했는지 명확하게 드러나지 않는다.&lt;/p&gt;
&lt;p&gt;따라서 이러한 기술지원 로직들은 수동빈 등록으로 설정정보에 바로 나타나게 하는 것이 유지보수에 유리하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;출처 : 인프런 스프링 백엔드 핵심기본, 스프링 인 액션&lt;/h3&gt;</description>
      <category>Backend/Spring</category>
      <category>스프링 구조</category>
      <category>스프링 인 액션</category>
      <category>스프링이란</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/142</guid>
      <comments>https://katastrophe.tistory.com/142#entry142comment</comments>
      <pubDate>Sat, 7 May 2022 02:26:57 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [3085] 사탕게임 JAVA</title>
      <link>https://katastrophe.tistory.com/141</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3085&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/3085&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1651767831811&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;3085번: 사탕 게임&quot; data-og-description=&quot;예제 3의 경우 4번 행의 Y와 C를 바꾸면 사탕 네 개를 먹을 수 있다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/3085&quot; data-og-url=&quot;https://www.acmicpc.net/problem/3085&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCb9x6/hyOhvcd5rd/B9DGdn9YKKfOfjXubygYp0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3085&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/3085&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCb9x6/hyOhvcd5rd/B9DGdn9YKKfOfjXubygYp0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;3085번: 사탕 게임&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;예제 3의 경우 4번 행의 Y와 C를 바꾸면 사탕 네 개를 먹을 수 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1355&quot; data-origin-height=&quot;847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mwckv/btrBm8gOPQ1/eHAkVThFF9ZRWe6458bqqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mwckv/btrBm8gOPQ1/eHAkVThFF9ZRWe6458bqqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mwckv/btrBm8gOPQ1/eHAkVThFF9ZRWe6458bqqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmwckv%2FbtrBm8gOPQ1%2FeHAkVThFF9ZRWe6458bqqk%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;1355&quot; height=&quot;847&quot; data-origin-width=&quot;1355&quot; data-origin-height=&quot;847&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;1359&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch1mhQ/btrBltrRAmz/eCARDoWLmxjVE0cs37o3R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch1mhQ/btrBltrRAmz/eCARDoWLmxjVE0cs37o3R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch1mhQ/btrBltrRAmz/eCARDoWLmxjVE0cs37o3R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch1mhQ%2FbtrBltrRAmz%2FeCARDoWLmxjVE0cs37o3R0%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;1359&quot; height=&quot;860&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af; color: #000000;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;탐색 위치를 p1 이라고 하면 x 방향으로 1 또는 y 방향으로 1 다음인 p2 와 자리값을 바꾸고&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;주어진 2차원 배열을 모두 탐색해서 연속되는 문자의 최대 값을 갱신시키면 된다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;N&amp;lt;=50 이기 때문에 충분히 시간내에 탐색할 수 있다. =&amp;gt; lis 알고리즘 사용 x&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1651768097153&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {

    static int max = 1;
    static int n;
    static char[][] map;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        n = Integer.parseInt(br.readLine());
        map = new char[n][n];

        for(int i=0;i&amp;lt;n;i++){
            map[i] = br.readLine().toCharArray();
        } // end input

        for(int i=0;i&amp;lt;n;i++){
            for(int j=0;j&amp;lt;n;j++){

                if(isRange(j+1,i)) {
                    swap(new Point(j,i),new Point(j+1,i));
                }

                if(isRange(j,i+1)) {
                    swap(new Point(j,i),new Point(j,i+1));
                }
            }
        }

        System.out.println(max);
    }

    private static void swap(Point p1,Point p2) {  // 0:x 1:y
        char cur = map[p1.y][p1.x];
        char next = map[p2.y][p2.x];
        // swap
        map[p1.y][p1.x] = next;
        map[p2.y][p2.x] = cur;
        search(); // 연속 되는 사탕 찾기
        // restore
        map[p1.y][p1.x] = cur;
        map[p2.y][p2.x] = next;

    }

    private static void search() {

        int cnt = 1;

        for(int i=0;i&amp;lt;n;i++){ // x 방향 진행
            for(int j=0;j&amp;lt;n;j++){

                if(isRange(j+1,i) &amp;amp;&amp;amp; map[i][j]==map[i][j+1]){ // 연속으로 같으면 cnt 수 증가
                    cnt++;
                    max = Math.max(max,cnt);
                }else{
                    cnt=1;
                }
            }
        }

        for(int i=0;i&amp;lt;n;i++){ // y 방향 진행
            for(int j=0;j&amp;lt;n;j++){

                if(isRange(i,j+1) &amp;amp;&amp;amp; map[j][i]==map[j+1][i]){
                    cnt++;
                    max = Math.max(max,cnt);
                }else{
                    cnt=1;
                }
            }
        }
    }

    static boolean isRange(int x,int y){
        return x&amp;gt;=0&amp;amp;&amp;amp;y&amp;gt;=0&amp;amp;&amp;amp;x&amp;lt;n&amp;amp;&amp;amp;y&amp;lt;n;
    }
    static class Point{
        int x,y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}​&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wu7Kn/btrBiHysiLl/pivnhmYrsgDd97ritVlHAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wu7Kn/btrBiHysiLl/pivnhmYrsgDd97ritVlHAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wu7Kn/btrBiHysiLl/pivnhmYrsgDd97ritVlHAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwu7Kn%2FbtrBiHysiLl%2FpivnhmYrsgDd97ritVlHAk%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;1321&quot; height=&quot;315&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 3085 java</category>
      <category>백준 사탕게임 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/141</guid>
      <comments>https://katastrophe.tistory.com/141#entry141comment</comments>
      <pubDate>Fri, 6 May 2022 01:28:43 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1935] 후위표기식2 JAVA</title>
      <link>https://katastrophe.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1935&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1935&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1651743358795&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;1935번: 후위 표기식2&quot; data-og-description=&quot;첫째 줄에 피연산자의 개수(1 &amp;le; N &amp;le; 26) 가 주어진다. 그리고 둘째 줄에는 후위 표기식이 주어진다. (여기서 피연산자는 A~Z의 영대문자이며, A부터 순서대로 N개의 영대문자만이 사용되며, 길이&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1935&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1935&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iF7g2/hyOhw2XGe4/7ikXDorVZCVkkQ7lxDYdwk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1935&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1935&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iF7g2/hyOhw2XGe4/7ikXDorVZCVkkQ7lxDYdwk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1935번: 후위 표기식2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 피연산자의 개수(1 &amp;le; N &amp;le; 26) 가 주어진다. 그리고 둘째 줄에는 후위 표기식이 주어진다. (여기서 피연산자는 A~Z의 영대문자이며, A부터 순서대로 N개의 영대문자만이 사용되며, 길이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;color: #000000; background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H4zjc/btrBipEhjzg/TwMIDpifZn3gN2XKOvGKUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H4zjc/btrBipEhjzg/TwMIDpifZn3gN2XKOvGKUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H4zjc/btrBipEhjzg/TwMIDpifZn3gN2XKOvGKUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH4zjc%2FbtrBipEhjzg%2FTwMIDpifZn3gN2XKOvGKUK%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;1217&quot; height=&quot;764&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;color: #000000; background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;후위 표기식 문제는 항상 stack을 이용한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 연산자 =&amp;gt; stack 에서 2개 꺼낸 피 연산자를 연산 후 stack에 결과 값을 push&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 피연산자 =&amp;gt; stack 에 push&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1651743492150&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int operandCount = Integer.parseInt(br.readLine());
        HashMap&amp;lt;Character, Double&amp;gt; operands = new HashMap&amp;lt;&amp;gt;();
        Stack&amp;lt;Double&amp;gt; stack = new Stack&amp;lt;&amp;gt;();

        char[] equations = br.readLine().toCharArray();

        for(int i=0;i&amp;lt;operandCount;i++){
            double value = Double.parseDouble(br.readLine());
            operands.put((char) ('A'+i),value);
        } // end input

        for (char ch : equations) {

            if(!isOperator(ch)){ // 연산자인경우
                stack.push(operands.get(ch));
            }else { // 피 연산자인 경우

                Double op1 = stack.pop();
                Double op2 = stack.pop();

                Double result = switch (ch) {
                    case '+' -&amp;gt; op2 + op1;
                    case '-' -&amp;gt; op2 - op1;
                    case '/' -&amp;gt; op2 / op1;
                    case '*' -&amp;gt; op2 * op1;
                    default -&amp;gt; 0.00;
                };
                stack.push(result);
            }
        }
        System.out.printf(&quot;%.2f&quot;,stack.pop());
    }

    static boolean isOperator(char ch){
        return ch&amp;lt;'A'||ch&amp;gt;'Z';
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 1935 java</category>
      <category>백준 후기 표기식2 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/140</guid>
      <comments>https://katastrophe.tistory.com/140#entry140comment</comments>
      <pubDate>Thu, 5 May 2022 18:38:29 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [17413] 단어뒤집기2 JAVA</title>
      <link>https://katastrophe.tistory.com/139</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17413&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/17413&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1651740452059&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;17413번: 단어 뒤집기 2&quot; data-og-description=&quot;문자열 S가 주어졌을 때, 이 문자열에서 단어만&amp;nbsp;뒤집으려고 한다. 먼저, 문자열 S는 아래와과 같은 규칙을 지킨다. 알파벳 소문자('a'-'z'), 숫자('0'-'9'), 공백(' '), 특수 문자('&amp;lt;', '&amp;gt;')로만 이루어져 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/17413&quot; data-og-url=&quot;https://www.acmicpc.net/problem/17413&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cf9Hkw/hyOhE0YQLm/yarDN3Ew27KyWi6HkBFrWk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17413&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/17413&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cf9Hkw/hyOhE0YQLm/yarDN3Ew27KyWi6HkBFrWk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;17413번: 단어 뒤집기 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문자열 S가 주어졌을 때, 이 문자열에서 단어만&amp;nbsp;뒤집으려고 한다. 먼저, 문자열 S는 아래와과 같은 규칙을 지킨다. 알파벳 소문자('a'-'z'), 숫자('0'-'9'), 공백(' '), 특수 문자('&amp;lt;', '&amp;gt;')로만 이루어져&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1377&quot; data-origin-height=&quot;873&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KxivQ/btrBmvv3BDt/WdUMRsk2GLLUSAgZmX6fUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KxivQ/btrBmvv3BDt/WdUMRsk2GLLUSAgZmX6fUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KxivQ/btrBmvv3BDt/WdUMRsk2GLLUSAgZmX6fUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKxivQ%2FbtrBmvv3BDt%2FWdUMRsk2GLLUSAgZmX6fUk%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;1377&quot; height=&quot;873&quot; data-origin-width=&quot;1377&quot; data-origin-height=&quot;873&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;1387&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCT9AD/btrBlMdJ5on/6yK9jhWfDKPBvaeUbrsg3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCT9AD/btrBlMdJ5on/6yK9jhWfDKPBvaeUbrsg3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCT9AD/btrBlMdJ5on/6yK9jhWfDKPBvaeUbrsg3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCT9AD%2FbtrBlMdJ5on%2F6yK9jhWfDKPBvaeUbrsg3K%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;1387&quot; height=&quot;666&quot; data-origin-width=&quot;1387&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;태그 안에 있는 문자열들을 제외한 나머지들을 뒤집어 출력하는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;기본적으로 단어들을 모아서 출력하기 때문에&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 태그 시작점 &quot;&amp;lt;&quot; 또는 공백 &quot; &quot; 일 경우 =&amp;gt; 모아논 문자열을 뒤집어서 Builder에 넣는다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;1.1 &quot;&amp;lt;&quot; 일 경우 태그 문자열을 정방향 출력이기 때문에 그대로 출력 하라는 flag 설정&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;1.2 &quot; &quot; 일 경우 뒤집은 문자열을 삽입&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 태그가 끝날경우 =&amp;gt; flag 초기화&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. flag = true 인 경우 =&amp;gt; 태그 안 문자열이므로 Builder에 삽입&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 일반 문자열일 경우 =&amp;gt; 문자하나씩 모아서 문자열에 이어붙임&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1~4 번이 다 끝나면 Builder에 남아 있는 문자열이 있을 수 있기 때문에 1.2 과정을 한번 더 진행&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1651740887438&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;

import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] input = br.readLine().split(&quot;&quot;);

        boolean stream = false;
        StringBuilder sb = new StringBuilder();
        StringBuilder reverseStr = new StringBuilder();
        for (String s : input) {

            if (s.equals(&quot;&amp;lt;&quot;)) {
                sb.append(reverseStr.reverse()); // reverse 한 값을 추가
                sb.append(s); //공백은 정방향으로 추가
                reverseStr = new StringBuilder(); // revereString 추가
                stream = true; // 태그 stream 시작
            } else if (s.equals(&quot;&amp;gt;&quot;)) { // 태그 끝
                sb.append(s);
                stream = false; // 태그 stream 종료
            } else if (stream) { // 태그 안 문자열이면 그대로 추가
                sb.append(s);
            } else if (s.equals(&quot; &quot;)) { // 공백을 만날 경우 reverseString 추가
                sb.append(reverseStr.reverse());
                sb.append(s);
                reverseStr = new StringBuilder();
            } else { // 일반 문자열인 경우
                reverseStr.append(s);
            }
        }
        sb.append(reverseStr.reverse()); // reverseString 에 남은 문자열 추가
        System.out.println(sb.toString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hli7j/btrBhQ3tiGl/WHUjcSYKvXy3oS82Ui0itk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hli7j/btrBhQ3tiGl/WHUjcSYKvXy3oS82Ui0itk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hli7j/btrBhQ3tiGl/WHUjcSYKvXy3oS82Ui0itk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHli7j%2FbtrBhQ3tiGl%2FWHUjcSYKvXy3oS82Ui0itk%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;1316&quot; height=&quot;356&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>단어뒤집기2 java</category>
      <category>백준 17413</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/139</guid>
      <comments>https://katastrophe.tistory.com/139#entry139comment</comments>
      <pubDate>Thu, 5 May 2022 17:55:15 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [12100] 2048(Easy) JAVA</title>
      <link>https://katastrophe.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/12100&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1651075594672&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;12100번: 2048 (Easy)&quot; data-og-description=&quot;첫째 줄에 보드의 크기 N (1 &amp;le; N &amp;le; 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/12100&quot; data-og-url=&quot;https://www.acmicpc.net/problem/12100&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UJPPH/hyObkiL9ZF/Th9IZ6vsp5uIuDc4bbPCS0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/dtbBUW/hyOcwaQKgU/2888TXKJ8a6Tll8MferNKk/img.png?width=1058&amp;amp;height=1056&amp;amp;face=0_0_1058_1056,https://scrap.kakaocdn.net/dn/lLNBk/hyObtGNSn3/MJR7IrEcQDwECqbAXFdeR0/img.png?width=1050&amp;amp;height=1064&amp;amp;face=0_0_1050_1064&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/12100&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UJPPH/hyObkiL9ZF/Th9IZ6vsp5uIuDc4bbPCS0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/dtbBUW/hyOcwaQKgU/2888TXKJ8a6Tll8MferNKk/img.png?width=1058&amp;amp;height=1056&amp;amp;face=0_0_1058_1056,https://scrap.kakaocdn.net/dn/lLNBk/hyObtGNSn3/MJR7IrEcQDwECqbAXFdeR0/img.png?width=1050&amp;amp;height=1064&amp;amp;face=0_0_1050_1064');&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;12100번: 2048 (Easy)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 보드의 크기 N (1 &amp;le; N &amp;le; 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyEfwe/btrACMNZNkE/4Af97kNSpXbYfk42Rioj61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyEfwe/btrACMNZNkE/4Af97kNSpXbYfk42Rioj61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyEfwe/btrACMNZNkE/4Af97kNSpXbYfk42Rioj61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyEfwe%2FbtrACMNZNkE%2F4Af97kNSpXbYfk42Rioj61%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;1241&quot; height=&quot;840&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;완전탐색으로 풀어내면 되는데 2차원 이상 배열은 복사를&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1차원 배열 단위로 고쳐서 .clone() 메서드를 사용해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;dfs탐색하기 전에 기존 배열을 어디에 저장해놓고 dfs 탐색이 끝나면 저장된 배열을 원복하면서 탐색한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;연쇄적으로 합쳐질 수 없기 때문에 큐를 하나 선언해서 하나씩 poll 하면서 병합을 진행한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 끝이 0 이면 큐에서 하나 빼서 할당&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 할당된 자리랑 큐에서 하나 뺀 값이 같으면 병합 후 index 증가&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 그 외인 경우 ( 할당된 값이랑 큐에서 꺼낸 값이 다를 때 ) index 증가시키고 큐에서 하나 뺀 값을 할당&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1651077277900&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package baekjoon;

import static java.util.Arrays.*;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Stack;
import java.util.stream.Collectors;

public class Main {

    static int n;
    static int[][] map;
    static int ans = 0;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        map = new int[n][n];
        for (int i = 0; i &amp;lt; n; i++) {
            map[i] = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt).toArray();
        } // end input

        rotate(0);
        System.out.println(ans);
    }

    private static void rotate(int count) { // dfs

        if(count==5){ // 5번 움직였을 때 종결
            for(int i=0;i&amp;lt;n;i++)
                for(int j=0;j&amp;lt;n;j++)
                    ans = Math.max(ans,map[i][j]); // max 값 갱신
            return;
        }

        int[][] nextMap = new int[n][n]; // 이전 상태로 되돌리기 위한 배열 복사
        for(int k=0;k&amp;lt;n;k++)
            nextMap[k] = map[k].clone();

        for(int i=0;i&amp;lt;4;i++){ // 우,좌,하,상
            move(i);
            rotate(count+1);
            for(int k=0;k&amp;lt;n;k++)
                map[k] = nextMap[k].clone(); // 원복
        }
    }

    static void move(int dir) { // 해당 방향에서 움직이는 함수

        LinkedList&amp;lt;Integer&amp;gt; q = new LinkedList&amp;lt;&amp;gt;(); // 한번 움직임에 두 번 이상 합쳐짐을 방지하기 위한 큐

        if (dir == 0) { // 우
            for (int i = 0; i &amp;lt; n; i++) {
                for (int j = 0; j &amp;lt; n; j++) {
                    if (map[i][j] != 0)
                        q.add(map[i][j]); // 큐에 삽입
                    map[i][j] = 0;
                }

                int index = 0; // 0부터 시작
                while (!q.isEmpty()) {
                    Integer cur = q.poll();

                    if (map[i][index] == 0) { // 0 이면
                        map[i][index] = cur;
                    } else if (map[i][index] == cur) { // 병합하기
                        map[i][index] = cur * 2;
                        index++;
                    } else { // 합쳐질 수 없는 경우 그대로 이동만
                        index++;
                        map[i][index] = cur;
                    }
                }
            }
        } else if (dir == 1) { // 좌
            for (int i = 0; i &amp;lt; n; i++) {
                for (int j = n - 1; j &amp;gt;= 0; j--) {
                    if (map[i][j] != 0)
                        q.add(map[i][j]);
                    map[i][j] = 0;
                }

                int index = n - 1;
                while (!q.isEmpty()) {
                    Integer cur = q.poll();

                    if (map[i][index] == 0) {
                        map[i][index] = cur;
                    } else if (map[i][index] == cur) {
                        map[i][index] = cur * 2;
                        index--;
                    } else {
                        index--;
                        map[i][index] = cur;
                    }
                }
            }
        } else if (dir == 2) { // 하
            for (int i = 0; i &amp;lt; n; i++) {
                for (int j = n - 1; j &amp;gt;= 0; j--) {
                    if (map[j][i] != 0)
                        q.add(map[j][i]);
                    map[j][i] = 0;
                }

                int index = n - 1;
                while (!q.isEmpty()) {
                    Integer cur = q.poll();

                    if (map[index][i] == 0) {
                        map[index][i] = cur;
                    } else if (map[index][i] == cur) {
                        map[index][i] = cur * 2;
                        index--;
                    } else {
                        index--;
                        map[index][i] = cur;
                    }
                }
            }
        } else if (dir == 3) { // 상
            for (int i = 0; i &amp;lt; n; i++) {
                for (int j = 0; j &amp;lt; n; j++) {
                    if (map[j][i] != 0)
                        q.add(map[j][i]);
                    map[j][i] = 0;
                }

                int index = 0;
                while (!q.isEmpty()) {
                    Integer cur = q.poll();

                    if (map[index][i] == 0) {
                        map[index][i] = cur;
                    } else if (map[index][i] == cur) {
                        map[index][i] = cur * 2;
                        index++;
                    } else {
                        index++;
                        map[index][i] = cur;
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFLjF0/btrAGgmiujq/XcPAfJPRxHnP2bxNNzu741/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFLjF0/btrAGgmiujq/XcPAfJPRxHnP2bxNNzu741/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFLjF0/btrAGgmiujq/XcPAfJPRxHnP2bxNNzu741/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFLjF0%2FbtrAGgmiujq%2FXcPAfJPRxHnP2bxNNzu741%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;1191&quot; height=&quot;310&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 12100 java</category>
      <category>백준 2048 java</category>
      <category>백트래킹</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/138</guid>
      <comments>https://katastrophe.tistory.com/138#entry138comment</comments>
      <pubDate>Thu, 28 Apr 2022 01:35:12 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [16198] 에너지 모으기JAVA</title>
      <link>https://katastrophe.tistory.com/137</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16198&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/16198&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650988488017&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;16198번: 에너지 모으기&quot; data-og-description=&quot;N개의 에너지 구슬이 일렬로 놓여져 있고, 에너지 구슬을 이용해서 에너지를 모으려고 한다. i번째 에너지 구슬의 무게는 Wi이고, 에너지를 모으는 방법은 다음과 같으며, 반복해서 사용할 수 있&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/16198&quot; data-og-url=&quot;https://www.acmicpc.net/problem/16198&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zoBRG/hyObqo3N2S/yBE8v9K10CkrkIgDKOcIHK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16198&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/16198&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zoBRG/hyObqo3N2S/yBE8v9K10CkrkIgDKOcIHK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;16198번: 에너지 모으기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;N개의 에너지 구슬이 일렬로 놓여져 있고, 에너지 구슬을 이용해서 에너지를 모으려고 한다. i번째 에너지 구슬의 무게는 Wi이고, 에너지를 모으는 방법은 다음과 같으며, 반복해서 사용할 수 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hxcs6/btrAAbL80uP/a4qaHMbYybMlinn9wTSvU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hxcs6/btrAAbL80uP/a4qaHMbYybMlinn9wTSvU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hxcs6/btrAAbL80uP/a4qaHMbYybMlinn9wTSvU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHxcs6%2FbtrAAbL80uP%2Fa4qaHMbYybMlinn9wTSvU0%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;1231&quot; height=&quot;885&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;885&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;DFS 완전 탐색 문제이다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1 2 3 4 가 주어질 때&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(3) 번을 고를 경우 =&amp;gt; energy += 2*4 가 되고 1 2 4 가 남는다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1 4 는 고를 수 없기 때문에 남은 2번을 고르면 energy += 1*4 를 하면 최종적으로 12가 나온다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;List 하나 선언해서 DFS를 돌리고 해당 위치 x 를 remove 한 뒤&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;백트래킹을해서 다시 x 를 넣어 주는 식으로 하는 것이 일반적인 풀이다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;다른풀이)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;union-find 가 생각이나서 next[] 라는 배열 하나 더 선언 한 뒤에 자기 자신 위치를 초기로 가지고 있다가&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;left 나 right 값이 방문 되어 있을 경우 그 다음 left나 right 를 찾을 수 있는 메서드를 이용하였다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;백트래킹 할 때는 해당위치가 x라고 하면 next[x]=x 이런 식으로 인덱스를 복구도 같이 진행.&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1650988935969&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package baekjoon;

import static java.util.Arrays.*;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Stack;
import java.util.stream.Collectors;

public class Main {

    static int maxEnergy = Integer.MIN_VALUE;
    static int n;
    static int[] energy;
    static boolean[] visit;
    static int[] next;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        n = Integer.parseInt(br.readLine());
        energy = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt).toArray();
        visit = new boolean[n];
        next = new int[n];
        for (int i = 0; i &amp;lt; n; i++)
            next[i] = i; // 현재 위치는 자기 자신

        getEnergy(0, 0);
        System.out.println(maxEnergy);
    }

    public static void getEnergy(int depth, int totalEnergy) {

        if (depth == n - 2) { // 최대값 갱신
            maxEnergy = Math.max(totalEnergy, maxEnergy);
        }

        for (int i = 1; i &amp;lt; n - 1; i++) {

            if (!visit[i]) { // 만약 방문 안한 곳이면
                visit[i] = true;
                getEnergy(depth + 1, totalEnergy +
                        energy[findRight(i)] * energy[findLeft(i)]); // 좌우로 방문된 곳 제외하고 새로 찾은 값
                visit[i] = false;
                next[i] = i;
            }
        }
    }

    public static int findRight(int x) {

        if (!visit[x]) // 현재 위치가 방문안한 곳이면 해당 인덱스 return
            return x;
        return next[x] = findRight(x + 1); // 다음 위치 return
    }

    public static int findLeft(int x) {

        if (!visit[x])
            return x;
        return next[x] = findLeft(x - 1);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1287&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TbRzE/btrAxGl3qr0/axbWUkyHi2fmtZOkXp2aZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TbRzE/btrAxGl3qr0/axbWUkyHi2fmtZOkXp2aZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TbRzE/btrAxGl3qr0/axbWUkyHi2fmtZOkXp2aZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTbRzE%2FbtrAxGl3qr0%2FaxbWUkyHi2fmtZOkXp2aZk%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;1287&quot; height=&quot;313&quot; data-origin-width=&quot;1287&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 16198 java</category>
      <category>백준 에너지모으기java</category>
      <category>백트래킹</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/137</guid>
      <comments>https://katastrophe.tistory.com/137#entry137comment</comments>
      <pubDate>Wed, 27 Apr 2022 01:03:12 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1406] 에디터 JAVA</title>
      <link>https://katastrophe.tistory.com/136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1406&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1406&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650984417943&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;1406번: 에디터&quot; data-og-description=&quot;첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1406&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1406&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1406&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1406&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1406번: 에디터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euBaAA/btrAzbeoakY/E8fROmR32dksm5b1lzeTk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euBaAA/btrAzbeoakY/E8fROmR32dksm5b1lzeTk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euBaAA/btrAzbeoakY/E8fROmR32dksm5b1lzeTk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuBaAA%2FbtrAzbeoakY%2FE8fROmR32dksm5b1lzeTk1%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;1212&quot; height=&quot;815&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시간제한을 보면 매우 빡빡한 것을 알 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf1N23/btrAx74LhkB/rFBucP1OZ9fKt9bjQmgJ6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf1N23/btrAx74LhkB/rFBucP1OZ9fKt9bjQmgJ6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf1N23/btrAx74LhkB/rFBucP1OZ9fKt9bjQmgJ6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf1N23%2FbtrAx74LhkB%2FrFBucP1OZ9fKt9bjQmgJ6K%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;1284&quot; height=&quot;206&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;실패한 원인은 처음에 LinkedList 를 썼으나 결국엔 조건을 만족하기 위해서 index를 타야 했기 때문&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;입력이 매우 길기 때문에 index를 탈 생각을 하면 안된다 ( StringBuilder.insert 연산도 마찬가지)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;따라서 2개의 스택만으로 모든 걸 해결해야 하는 문제이다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1650984682051&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package baekjoon;

import static java.util.Arrays.*;

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        Stack&amp;lt;String&amp;gt; left = stream(br.readLine().split(&quot;&quot;))
                .collect(Collectors.toCollection(Stack::new));
        Stack&amp;lt;String&amp;gt; right = new Stack&amp;lt;&amp;gt;();
        int queryCount = Integer.parseInt(br.readLine());

        for(int i=0;i&amp;lt;queryCount;i++){
            String[] input = br.readLine().split(&quot; &quot;);
            String query = input[0];

            if(query.equals(&quot;L&quot;) &amp;amp;&amp;amp; !left.isEmpty()){
                right.add(left.pop());
            }else if(query.equals(&quot;D&quot;) &amp;amp;&amp;amp; !right.isEmpty()){
                left.add(right.pop());
            }else if(query.equals(&quot;B&quot;) &amp;amp;&amp;amp; !left.isEmpty()){
                left.pop();
            }else if(query.equals(&quot;P&quot;) ){
                left.add(input[1]);
            }
        }
        
        StringBuffer ans = new StringBuffer();
        left.forEach(ans::append);
        while (!right.isEmpty())
            ans.append(right.pop());
        System.out.println(ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Akvtx/btrAtcFR3kd/8JukygtEZfcscaYna1DRM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Akvtx/btrAtcFR3kd/8JukygtEZfcscaYna1DRM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Akvtx/btrAtcFR3kd/8JukygtEZfcscaYna1DRM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAkvtx%2FbtrAtcFR3kd%2F8JukygtEZfcscaYna1DRM0%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;1307&quot; height=&quot;303&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 1406 java</category>
      <category>백준 에디터 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/136</guid>
      <comments>https://katastrophe.tistory.com/136#entry136comment</comments>
      <pubDate>Tue, 26 Apr 2022 23:51:42 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [2504] 괄호의 값JAVA</title>
      <link>https://katastrophe.tistory.com/135</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2504&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/2504&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650467925559&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;2504번: 괄호의 값&quot; data-og-description=&quot;4개의 기호 &amp;lsquo;(&amp;rsquo;, &amp;lsquo;)&amp;rsquo;, &amp;lsquo;[&amp;rsquo;, &amp;lsquo;]&amp;rsquo;를 이용해서 만들어지는 괄호열 중에서 올바른 괄호열이란 다음과 같이 정의된다. 한 쌍의 괄호로만 이루어진 &amp;lsquo;()&amp;rsquo;와 &amp;lsquo;[]&amp;rsquo;는 올바른 괄호열이다.&amp;nbsp; 만일 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2504&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2504&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/E3Q4p/hyN6TddgUT/TTwQ4OtQepIhHK6VQlMuN1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2504&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2504&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/E3Q4p/hyN6TddgUT/TTwQ4OtQepIhHK6VQlMuN1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;2504번: 괄호의 값&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;4개의 기호 &amp;lsquo;(&amp;rsquo;, &amp;lsquo;)&amp;rsquo;, &amp;lsquo;[&amp;rsquo;, &amp;lsquo;]&amp;rsquo;를 이용해서 만들어지는 괄호열 중에서 올바른 괄호열이란 다음과 같이 정의된다. 한 쌍의 괄호로만 이루어진 &amp;lsquo;()&amp;rsquo;와 &amp;lsquo;[]&amp;rsquo;는 올바른 괄호열이다.&amp;nbsp; 만일&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;921&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CW8oi/btrzWqSuVLo/4AElZifGKYIpEppqRjifG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CW8oi/btrzWqSuVLo/4AElZifGKYIpEppqRjifG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CW8oi/btrzWqSuVLo/4AElZifGKYIpEppqRjifG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCW8oi%2FbtrzWqSuVLo%2F4AElZifGKYIpEppqRjifG1%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;1191&quot; height=&quot;921&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;921&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;괄호 짝이 맞는 지 검사하는 작업에 더해서 괄호에 따른 연산을 추가로 하는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 열린 괄호인 경우 :&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;- stack 에 push 한 뒤 임시 변수에 곱 연산을 한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 닫힌 괄호인 경우 :&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;2.1 이전 괄호가 짝이 맞는 열린 괄호인 경우&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 더 이상 곱 연산이 적용 되지 않는 원소이므로 최종 결과 값에 더해준다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;2.2 stack의 맨 위 원소와 짝이 맞지 않거나 stack 이 비어 있는 경우&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 잘못된 괄호이므로 break&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;- stack 에서 pop 한 뒤 괄호에 맞는 값을 임시 변수에 나누기 연산을 한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;- 루프를 다 돌 동안에 stack 이 비어 있지 않으면 짝이 맞지 않으므로 break&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1650468586073&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
import static java.util.Arrays.*;

import java.io.*;
import java.util.ArrayList;
import java.util.Stack;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        ArrayList&amp;lt;String&amp;gt; words = stream(br.readLine().split(&quot;&quot;))
                .collect(Collectors.toCollection(ArrayList::new));

        int answer = 0;
        Stack&amp;lt;String&amp;gt; stack = new Stack&amp;lt;&amp;gt;();
        String prev = &quot;&quot;;
        int cur = 1; // 1에서 부터 시작

        loop:
        for (String word : words) {

            switch (word) {
                case &quot;(&quot; -&amp;gt; {   // 열리는 괄호 일때
                    cur *= 2;
                    stack.push(word);
                }
                case &quot;[&quot; -&amp;gt; {
                    cur *= 3;
                    stack.push(word);
                }
                case &quot;)&quot; -&amp;gt; {  // 닫히는 괄호 일때

                    if (stack.isEmpty() || !stack.peek().equals(&quot;(&quot;)) { // 잘못 된 괄호이면 break
                        answer = 0;
                        break loop;
                    } else if (prev.equals(&quot;(&quot;)) // 중간 정산
                        answer += cur;
                    stack.pop();
                    cur /= 2;
                }
                case &quot;]&quot; -&amp;gt; {
                    if (stack.isEmpty() || !stack.peek().equals(&quot;[&quot;)) {
                        answer = 0;
                        break loop;
                    } else if (prev.equals(&quot;[&quot;))
                        answer += cur;
                    stack.pop();
                    cur /= 3;
                }
            }
            prev = word; // 중간 정산을 위한 이전 값 기록
        }

        if(!stack.isEmpty()) // 괄호짝이 남는 경우 =&amp;gt; 잘못된 괄호
            answer = 0;
        System.out.println(answer);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1175&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8jyMP/btrz2rhkGrF/RIe3K8B83AROtBT0eemkEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8jyMP/btrz2rhkGrF/RIe3K8B83AROtBT0eemkEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8jyMP/btrz2rhkGrF/RIe3K8B83AROtBT0eemkEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8jyMP%2Fbtrz2rhkGrF%2FRIe3K8B83AROtBT0eemkEK%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;1175&quot; height=&quot;274&quot; data-origin-width=&quot;1175&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 2504 java</category>
      <category>백준 괄호의값 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/135</guid>
      <comments>https://katastrophe.tistory.com/135#entry135comment</comments>
      <pubDate>Thu, 21 Apr 2022 00:30:09 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [18111] 마인크래프트 JAVA</title>
      <link>https://katastrophe.tistory.com/134</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/18111&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/18111&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650443091966&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;18111번: 마인크래프트&quot; data-og-description=&quot;팀 레드시프트는&amp;nbsp;대회 준비를 하다가 지루해져서 샌드박스 게임인 &amp;lsquo;마인크래프트&amp;rsquo;를 켰다. 마인크래프트는 1 &amp;times;&amp;nbsp;1 &amp;times;&amp;nbsp;1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/18111&quot; data-og-url=&quot;https://www.acmicpc.net/problem/18111&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bqm5mY/hyN6ZK8veg/9wsVv7npnBpbVzhMJHBEP0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/ca4PpQ/hyN6QglumU/YGcxoJMbovtLYMElwDtYBK/img.png?width=2048&amp;amp;height=1884&amp;amp;face=0_0_2048_1884&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/18111&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/18111&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bqm5mY/hyN6ZK8veg/9wsVv7npnBpbVzhMJHBEP0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/ca4PpQ/hyN6QglumU/YGcxoJMbovtLYMElwDtYBK/img.png?width=2048&amp;amp;height=1884&amp;amp;face=0_0_2048_1884');&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;18111번: 마인크래프트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;팀 레드시프트는&amp;nbsp;대회 준비를 하다가 지루해져서 샌드박스 게임인 &amp;lsquo;마인크래프트&amp;rsquo;를 켰다. 마인크래프트는 1 &amp;times;&amp;nbsp;1 &amp;times;&amp;nbsp;1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GJxg0/btrzVBlEkOp/OWlmoABjpYaGKL03MW8ntK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GJxg0/btrzVBlEkOp/OWlmoABjpYaGKL03MW8ntK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GJxg0/btrzVBlEkOp/OWlmoABjpYaGKL03MW8ntK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGJxg0%2FbtrzVBlEkOp%2FOWlmoABjpYaGKL03MW8ntK%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;1224&quot; height=&quot;695&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;695&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;1184&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx4Tq2/btrzZAMm2Ze/3Fqbs7ZgF5F0Chh61ckfk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx4Tq2/btrzZAMm2Ze/3Fqbs7ZgF5F0Chh61ckfk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx4Tq2/btrzZAMm2Ze/3Fqbs7ZgF5F0Chh61ckfk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx4Tq2%2FbtrzZAMm2Ze%2F3Fqbs7ZgF5F0Chh61ckfk0%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;1184&quot; height=&quot;749&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;749&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;단순 완전 탐색 + 구현 문제이다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;주어진 map 좌표에서 높이의 범위를 받은다음&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;범위 내 높이를 기준 h 에서 평평해지는 조건을 전부 찾아 &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;time과 inventory 조건을 만족할 때 마다 갱신을 시켜주면 된다.&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1650443793708&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;

import java.io.*;

public class Main {

    static int[][] map;
    static int h,w,b;
    static int fromHeight=Integer.MAX_VALUE, toHeight=0;
    static int minTime=Integer.MAX_VALUE,maxHeight;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt).toArray();
        h = input[0];w=input[1];b=input[2];
        map = new int[h][w];
        for(int y=0;y&amp;lt;h;y++){
            map[y] = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt).toArray();
            fromHeight = Math.min(stream(map[y]).min().getAsInt(), fromHeight);
            toHeight = Math.max(stream(map[y]).max().getAsInt(), toHeight); // 시간 초과 방지를 위해 블록 높이 range 설정
        } // end input

        for(int height = fromHeight; height&amp;lt;= toHeight; height++){

            int time=0;
            int inventory = b;

            for(int y=0;y&amp;lt;h;y++){
                for(int x=0;x&amp;lt;w;x++){ // 전체 좌표 탐색

                    if(time&amp;gt;minTime) // 최소 시간보다 높으면 continue
                        continue;

                    int diff = map[y][x]-height; // 블럭 높이 차이
                    if(diff&amp;gt;0){
                        time+=diff*2;
                        inventory += diff;
                    }else if(diff&amp;lt;0){
                        time-=diff;
                        inventory +=diff;
                    }
                }
            }
            if(inventory&amp;lt;0) // 채울 블럭이 없으면 pass
                continue;

            if(time&amp;lt;=minTime){ // 높이가 커지는 순으로 탐색하므로 time 이 같다면 갱신가능
                maxHeight = height;
                minTime = time;
            }
        }

        System.out.println(minTime+&quot; &quot;+maxHeight);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ED43j/btrzYFN9cPl/E6p7E6Ql2Oym0mReK8CWY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ED43j/btrzYFN9cPl/E6p7E6Ql2Oym0mReK8CWY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ED43j/btrzYFN9cPl/E6p7E6Ql2Oym0mReK8CWY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FED43j%2FbtrzYFN9cPl%2FE6p7E6Ql2Oym0mReK8CWY1%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;1161&quot; height=&quot;292&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 18111 java</category>
      <category>백준 마인크래프트 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/134</guid>
      <comments>https://katastrophe.tistory.com/134#entry134comment</comments>
      <pubDate>Wed, 20 Apr 2022 17:36:36 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1107] 리모컨 JAVA</title>
      <link>https://katastrophe.tistory.com/132</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1107&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1107&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649952443166&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;1107번: 리모컨&quot; data-og-description=&quot;첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 &amp;le; N &amp;le; 500,000)이 주어진다.&amp;nbsp;&amp;nbsp;둘째 줄에는 고장난 버튼의 개수 M (0 &amp;le; M &amp;le; 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1107&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1107&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GLu6D/hyN2BDPjbH/ziuHsUmAKIaMhIXi3TsgKK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1107&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1107&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GLu6D/hyN2BDPjbH/ziuHsUmAKIaMhIXi3TsgKK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1107번: 리모컨&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 &amp;le; N &amp;le; 500,000)이 주어진다.&amp;nbsp;&amp;nbsp;둘째 줄에는 고장난 버튼의 개수 M (0 &amp;le; M &amp;le; 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1361&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdmiQ8/btrzqzA2i1q/H8KkIMFEXcO0MrZw5Gk4rK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdmiQ8/btrzqzA2i1q/H8KkIMFEXcO0MrZw5Gk4rK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdmiQ8/btrzqzA2i1q/H8KkIMFEXcO0MrZw5Gk4rK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdmiQ8%2FbtrzqzA2i1q%2FH8KkIMFEXcO0MrZw5Gk4rK%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;1361&quot; height=&quot;865&quot; data-origin-width=&quot;1361&quot; data-origin-height=&quot;865&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;1382&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwXAOA/btrzn85IO0A/mT9YZmhLXGhZfcQg7y5KG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwXAOA/btrzn85IO0A/mT9YZmhLXGhZfcQg7y5KG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwXAOA/btrzn85IO0A/mT9YZmhLXGhZfcQg7y5KG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwXAOA%2Fbtrzn85IO0A%2FmT9YZmhLXGhZfcQg7y5KG1%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;1382&quot; height=&quot;517&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;완전탐색 문제이다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;목표채널 target 까지 0~999999 사이까지 모두 탐색해서 만약 고장난 버튼이 없이 갈 수 있다면&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그 채널에서 target 차이와 자릿수를 합한 결과를 반환하면 된다&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649954165824&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {

    static int n,target;
    static boolean[] isBroken = new boolean[10];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        target = Integer.parseInt(br.readLine());
        n = Integer.parseInt(br.readLine());
        if(n!=0){
            stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt)
                    .forEach(e-&amp;gt;isBroken[e]=true);
        }
        //end input

        int ans = Math.abs(100-target);

        for(int i=0;i&amp;lt;=999999;i++){

            String cur = Integer.toString(i);
            boolean check=false;

            for(int j=0;j&amp;lt;cur.length();j++){
                if(isBroken[cur.charAt(j) - '0']){
                    check=true; // 고장난 버튼이면 break
                    break;
                }
            }
            if(!check) // 고장안났으면 target 까지 거리 계산해서 갱신
                ans = Math.min(ans,cur.length()+Math.abs(i-target));
        }
        System.out.println(ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCxHv3/btrzoOlEQlp/mqWDTkyTdLoiDUqVXpGkl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCxHv3/btrzoOlEQlp/mqWDTkyTdLoiDUqVXpGkl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCxHv3/btrzoOlEQlp/mqWDTkyTdLoiDUqVXpGkl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCxHv3%2FbtrzoOlEQlp%2FmqWDTkyTdLoiDUqVXpGkl1%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;1312&quot; height=&quot;320&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 입력 0 인 경우 입력 받기에서 런타임 에러나는 것 때매 한참 헤맸다..&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 1107 java</category>
      <category>백준 리모컨 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/132</guid>
      <comments>https://katastrophe.tistory.com/132#entry132comment</comments>
      <pubDate>Fri, 15 Apr 2022 01:37:08 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [14500] 테트로미노 JAVA</title>
      <link>https://katastrophe.tistory.com/130</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14500&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/14500&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649920539328&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;14500번: 테트로미노&quot; data-og-description=&quot;폴리오미노란 크기가 1&amp;times;1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/14500&quot; data-og-url=&quot;https://www.acmicpc.net/problem/14500&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/j7hWE/hyN1LuaufW/Uv6tvKiVdWft1D7ULAAa01/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/bYALk7/hyN1vLzgNj/gwlwHgK9MPZjVs2GnApkQk/img.png?width=500&amp;amp;height=333&amp;amp;face=0_0_500_333&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14500&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/14500&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/j7hWE/hyN1LuaufW/Uv6tvKiVdWft1D7ULAAa01/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/bYALk7/hyN1vLzgNj/gwlwHgK9MPZjVs2GnApkQk/img.png?width=500&amp;amp;height=333&amp;amp;face=0_0_500_333');&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;14500번: 테트로미노&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;폴리오미노란 크기가 1&amp;times;1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dW7J6C/btrzoWbOM0B/VvUoUozkKyzzBPCqVPsUIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dW7J6C/btrzoWbOM0B/VvUoUozkKyzzBPCqVPsUIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dW7J6C/btrzoWbOM0B/VvUoUozkKyzzBPCqVPsUIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdW7J6C%2FbtrzoWbOM0B%2FVvUoUozkKyzzBPCqVPsUIK%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;1359&quot; height=&quot;846&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;846&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;1319&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dicL21/btrzjHUfWJ9/P8xfZvywFQuoAIujy8MKjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dicL21/btrzjHUfWJ9/P8xfZvywFQuoAIujy8MKjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dicL21/btrzjHUfWJ9/P8xfZvywFQuoAIujy8MKjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdicL21%2FbtrzjHUfWJ9%2FP8xfZvywFQuoAIujy8MKjK%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;1319&quot; height=&quot;782&quot; data-origin-width=&quot;1319&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;테트리스 블록을 암거나 하나 놓았을 때 그 블록이 위치한 격자 점수 합의 최대값을 구하는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;어느 한 점 x,y 에서 시작해서 깊이 우선탐색 DFS를 돌리면 블록 모양이 나온다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그런데 T 블록은 DFS로 구할 수 없으니 , 그 구간만 BFS로 구해야한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;BFS를 진행할 때 만약 4방향 모두 진행 할 수 있다면&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;중심좌표 x,y 의 값 + (4방향 좌표의 합) = sum 이라고 하면&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;sum - ( 4방향 각 좌표) 가 T 블록 점수 다른모양 4개가 나온다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;만약 4방향 모두 진행 못하고 3방향으로만 가능하면 블록은 1개이다.&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649920803121&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {

    static int w, h,max=0;
    static int[][] map;
    static boolean[][] visit;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt).toArray();
        h = input[0]; w = input[1];
        map = new int[h][w];
        visit = new boolean[h][w];
        for(int i=0;i&amp;lt;h;i++){
             map[i] = stream(br.readLine().split(&quot; &quot;))
                      .mapToInt(Integer::parseInt).toArray();
        } // end input

        for(int y=0;y&amp;lt;h;y++){
            for(int x=0;x&amp;lt;w;x++){
                visit[y][x] = true;
                dfs(x,y,1,map[y][x]); // 블록들 탐색
                bfs(x,y); // t 블록 탐색
                visit[y][x] = false;
            }
        }

        System.out.println(max);
    }

    static int[] dx={0,0,1,-1},dy={1,-1,0,0};
    private static void dfs(int x, int y, int depth, int sum) {

        if(depth==4){
            max = Math.max(max,sum);
            return;
        }

        for(int dir=0;dir&amp;lt;dx.length;dir++){
            int nextX = dx[dir]+x;
            int nextY = dy[dir]+y;

            if(!isRange(nextX,nextY) || visit[nextY][nextX])
                continue;

            visit[nextY][nextX]=true;
            dfs(nextX,nextY,depth+1,sum+map[nextY][nextX]);
            visit[nextY][nextX]=false;
        }

    }

    private static void bfs(int x, int y) {

        int sum = map[y][x];
        LinkedList&amp;lt;Integer&amp;gt; tBlocks = new LinkedList&amp;lt;&amp;gt;();

        for(int dir=0;dir&amp;lt;dx.length;dir++){
            int nextX = dx[dir]+x;
            int nextY = dy[dir]+y;

            if(!isRange(nextX,nextY))
                continue;

            tBlocks.add(map[nextY][nextX]);
            sum+=map[nextY][nextX];
        }

        if(tBlocks.size()==4){ // 4방향 모두 가능 한 경우
            for (Integer t : tBlocks)
                max = Math.max(max,sum-t);
        }else if(tBlocks.size()==3){ // 하나만 있는 경우
            max = Math.max(max,sum);
        }

    }

    private static boolean isRange(int x,int y){
        return x&amp;gt;=0&amp;amp;&amp;amp;y&amp;gt;=0&amp;amp;&amp;amp;x&amp;lt;w&amp;amp;&amp;amp;y&amp;lt;h;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2ayHR/btrzpbfEbE4/PtKbmI2ZZ15a8TpYvs8Nl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2ayHR/btrzpbfEbE4/PtKbmI2ZZ15a8TpYvs8Nl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2ayHR/btrzpbfEbE4/PtKbmI2ZZ15a8TpYvs8Nl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2ayHR%2FbtrzpbfEbE4%2FPtKbmI2ZZ15a8TpYvs8Nl1%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;1307&quot; height=&quot;321&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 14500 java</category>
      <category>백준 테트로미노 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/130</guid>
      <comments>https://katastrophe.tistory.com/130#entry130comment</comments>
      <pubDate>Thu, 14 Apr 2022 16:20:48 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1800] 인터넷 설치 JAVA</title>
      <link>https://katastrophe.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1800&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1800&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649831884713&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;1800번: 인터넷 설치&quot; data-og-description=&quot;첫 번째 줄에 N(1 &amp;le; N &amp;le; 1,000), 케이블선의 개수 P(1 &amp;le; P &amp;le; 10,000), 공짜로 제공하는 케이블선의 개수 K(0 &amp;le; K &amp;lt; N)이 주어진다. 다음 P개의 줄에는 케이블이 연결하는 두 컴퓨터 번호와 그 가격이 차&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1800&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1800&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NJWtL/hyN1B5mAHV/TUgoKSoKdi7XNksj7iK1S0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1800&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1800&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NJWtL/hyN1B5mAHV/TUgoKSoKdi7XNksj7iK1S0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1800번: 인터넷 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 줄에 N(1 &amp;le; N &amp;le; 1,000), 케이블선의 개수 P(1 &amp;le; P &amp;le; 10,000), 공짜로 제공하는 케이블선의 개수 K(0 &amp;le; K &amp;lt; N)이 주어진다. 다음 P개의 줄에는 케이블이 연결하는 두 컴퓨터 번호와 그 가격이 차&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wHNvO/btrziFOGIRR/dxZBODasknp8iZBTEMewK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wHNvO/btrziFOGIRR/dxZBODasknp8iZBTEMewK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wHNvO/btrziFOGIRR/dxZBODasknp8iZBTEMewK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwHNvO%2FbtrziFOGIRR%2FdxZBODasknp8iZBTEMewK0%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;1172&quot; height=&quot;810&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;처음 풀어보는 유형의 문제다. 아이디어를 못 떠올려서 도움을 받았다..&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;만약 1-3-2-5 이런식으로 연결 된다고 치면 각각 edge들에 대하여 가중치들은 4, 3, 9 원이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;여기서 k=1, 회선 하나는 공짜로 준다고 했으니 제일 비싼 9원을 빼면 남은건 4, 3 인데&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이들 중 제일 비싼 회선 값만 return 하므로 ans=4 이 답이다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;일단 출발점(1) 에서 부터 출발해서 탐색을 진행하는데 도착지(n) 까지 &lt;br /&gt;제일 싼 간선을 생각하면서 가야하므로 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;u&gt;다익스트라 알고리즘&lt;/u&gt;을 사용한다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그런데 &lt;u&gt;회선 몇개 까지 공짜로 봐주는지 체크를 해 가며 가야하므로 경우의 수가 너무 많다&lt;/u&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그래서 &lt;span style=&quot;color: #ee2323;&quot;&gt;특정 목표 추정치 Estimate 를 기준&lt;/span&gt;으로 도달 가능한지에 대한 여부로 범위를 좁혀가야 한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n7ifT/btrziv6wPbV/fF6e1LJ3LWdpd1MaiiKnWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n7ifT/btrziv6wPbV/fF6e1LJ3LWdpd1MaiiKnWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n7ifT/btrziv6wPbV/fF6e1LJ3LWdpd1MaiiKnWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn7ifT%2Fbtrziv6wPbV%2FfF6e1LJ3LWdpd1MaiiKnWk%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;463&quot; height=&quot;262&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;거기다가 가중치의 합 이라는 개념이 필요가 없다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그러므로 거리배열 &lt;span style=&quot;color: #ee2323;&quot;&gt;Dist[] 의 정의를 공짜 회선으로 전환한 갯수&lt;/span&gt;로 하였다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmZIl1/btrzenbaW6e/kJbktCxlYiZjlMv222oaok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmZIl1/btrzenbaW6e/kJbktCxlYiZjlMv222oaok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmZIl1/btrzenbaW6e/kJbktCxlYiZjlMv222oaok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmZIl1%2FbtrzenbaW6e%2FkJbktCxlYiZjlMv222oaok%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;437&quot; height=&quot;170&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그래서 최종적으로 Dist[n] 의 값이 K 안에 있으면 도달 가능한지에 대한 여부를 반환하면서&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이분탐색으로 도달가능하면 mid값 낮추고 도달 불가능하면 mid 값 높히는 식으로 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;답을 도출해야 한다.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649833569898&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {

    static int n,e,k;
    static int minEstimate=0,maxEstimate;
    static ArrayList&amp;lt;ArrayList&amp;lt;Node&amp;gt;&amp;gt; graph = new ArrayList&amp;lt;&amp;gt;();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
         int[] input = stream(br.readLine().split(&quot; &quot;))
                  .mapToInt(Integer::parseInt).toArray();
         n = input[0]; e = input[1]; k = input[2];

         for(int i=0;i&amp;lt;=n;i++)
             graph.add(new ArrayList&amp;lt;&amp;gt;());
         for(int i=0;i&amp;lt;e;i++){
             input = stream(br.readLine().split(&quot; &quot;))
                     .mapToInt(Integer::parseInt).toArray();
             graph.get(input[0]).add(new Node(input[1],input[2]));
             graph.get(input[1]).add(new Node(input[0],input[2]));
             maxEstimate = Math.max(maxEstimate,input[2]);
         }//end input

        int ans=-1;

        while (minEstimate&amp;lt;=maxEstimate){ //이분 탐색
            int midEstimate = (minEstimate+maxEstimate)/2;

            if(isArrive(midEstimate)) { //도달 가능하면 ans 갱신
                ans = midEstimate;
                maxEstimate = midEstimate - 1; // max 추정치 범위를 좁힘
            }
            else
                minEstimate = midEstimate + 1; // min 추정치 범위를 좁힘
        }
        System.out.println(ans); // 도달 불가능이면 -1
    }
    static boolean isArrive(int estimate){ // 추정치로 탐색 가능한지 탐색

        int[] dist = new int[n+1];
        Arrays.fill(dist,Integer.MAX_VALUE);
        dist[1]=0;

        PriorityQueue&amp;lt;Node&amp;gt; pq = new PriorityQueue&amp;lt;Node&amp;gt;((o1, o2) -&amp;gt; o1.weight-o2.weight);
        pq.add(new Node(1,0));

        while (!pq.isEmpty()){
            Node cur = pq.poll();

            if(dist[cur.node] &amp;lt; cur.weight)
                continue;

            for (Node next : graph.get(cur.node)) {

                int nextCount=cur.weight; // 공짜회선 개수

                if(estimate &amp;lt; next.weight) // 더 비싼 회선가격이 나오면
                    nextCount++; // 공짜 회선으로 전환

                if(dist[next.node] &amp;gt; nextCount){
                    dist[next.node]=nextCount;
                    pq.add(new Node(next.node,nextCount));
                }
            }
        }

        return dist[n]&amp;lt;=k; // 공짜회선 k개 보다 적은 개수이면 탐색 가능
    }
    static class Node {
        int node,weight;

        public Node(int node, int weight) {
            this.node = node;
            this.weight = weight;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOn5d5/btrzd9QYL7b/4nfM5uxxW7hpbNd6iqDH9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOn5d5/btrzd9QYL7b/4nfM5uxxW7hpbNd6iqDH9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOn5d5/btrzd9QYL7b/4nfM5uxxW7hpbNd6iqDH9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOn5d5%2Fbtrzd9QYL7b%2F4nfM5uxxW7hpbNd6iqDH9K%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;1156&quot; height=&quot;308&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>다익스트라</category>
      <category>백준 1800 java</category>
      <category>백준 인터넷 설치 java</category>
      <category>이분탐색</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/128</guid>
      <comments>https://katastrophe.tistory.com/128#entry128comment</comments>
      <pubDate>Wed, 13 Apr 2022 16:06:44 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1032] 명령 프롬프트 JAVA</title>
      <link>https://katastrophe.tistory.com/127</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1032&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1032&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649750727738&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;1032번: 명령 프롬프트&quot; data-og-description=&quot;첫째 줄에 파일 이름의 개수 N이 주어진다. 둘째 줄부터 N개의 줄에는 파일 이름이 주어진다. N은 50보다 작거나 같은 자연수이고 파일 이름의 길이는 모두 같고 길이는 최대 50이다. 파일이름은 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1032&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1032&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cykhdn/hyN1Ez5SFp/rnBVYkBaFOG4CIgymBR8mK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1032&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1032&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cykhdn/hyN1Ez5SFp/rnBVYkBaFOG4CIgymBR8mK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1032번: 명령 프롬프트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 파일 이름의 개수 N이 주어진다. 둘째 줄부터 N개의 줄에는 파일 이름이 주어진다. N은 50보다 작거나 같은 자연수이고 파일 이름의 길이는 모두 같고 길이는 최대 50이다. 파일이름은&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAMCC/btrzaWwQ2jW/KJ2gfHHuXLX9Kvt1rhIQh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAMCC/btrzaWwQ2jW/KJ2gfHHuXLX9Kvt1rhIQh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAMCC/btrzaWwQ2jW/KJ2gfHHuXLX9Kvt1rhIQh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAMCC%2FbtrzaWwQ2jW%2FKJ2gfHHuXLX9Kvt1rhIQh0%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;1322&quot; height=&quot;844&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;명령어 길이는 같으니 맨 위의 명령어를 대표로 잡고 세로로 비교&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;만약 다른 문자가 있으면 '?' 으로 치환, 그렇지 않으면 그 문자 그대로 쓰기&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649750882805&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        char[][] arr = new char[n][];

        for(int i=0;i&amp;lt;n;i++)
            arr[i] = br.readLine().toCharArray();
        // end input

        StringBuilder ans = new StringBuilder();

        for(int i=0;i&amp;lt;arr[0].length;i++){
            char peek = arr[0][i]; // 암거나 하나 대표문자로 선언

            for(int j=0;j&amp;lt;n;j++){
                if(peek!=arr[j][i]){ // 세로로 비교했을 때 다르면
                    peek = '?'; // peek 을 '?' 으로 치환
                    break;
                }
            }
            ans.append(peek);
        }
        System.out.println(ans.toString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LywuL/btrzdMGTFwt/pffF3KA7A7vjMSdsEz4nkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LywuL/btrzdMGTFwt/pffF3KA7A7vjMSdsEz4nkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LywuL/btrzdMGTFwt/pffF3KA7A7vjMSdsEz4nkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLywuL%2FbtrzdMGTFwt%2FpffF3KA7A7vjMSdsEz4nkk%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;1317&quot; height=&quot;314&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 1032 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/127</guid>
      <comments>https://katastrophe.tistory.com/127#entry127comment</comments>
      <pubDate>Tue, 12 Apr 2022 17:08:24 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name</title>
      <link>https://katastrophe.tistory.com/125</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjeONq/btrySONobHl/e2kuzmqrIkZGgpTENajUM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjeONq/btrySONobHl/e2kuzmqrIkZGgpTENajUM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjeONq/btrySONobHl/e2kuzmqrIkZGgpTENajUM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjeONq%2FbtrySONobHl%2Fe2kuzmqrIkZGgpTENajUM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;221&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;322&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JPA로 작성되었던 기존 CustomRepository Interface를 상속받은 RepositoryImpl을&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;QueryDsl 로 복잡한 쿼리를 해결하고 V2로 넘어가는 과정에서 문제가 발생하였다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 OrderRepositoryImpl를 남기고 싶어서 v1,v2 따로 나누기위해 Class 이름 뒤에 v1을 남기고&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;QueryDsl로 작성한 Repository의 뒤에 v2로 작성하여 돌렸더니 ..&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1811&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ItzcI/btryYmIz6ol/MyMyh6kv6ouOKkwqir5AD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ItzcI/btryYmIz6ol/MyMyh6kv6ouOKkwqir5AD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ItzcI/btryYmIz6ol/MyMyh6kv6ouOKkwqir5AD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FItzcI%2FbtryYmIz6ol%2FMyMyh6kv6ouOKkwqir5AD1%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;1811&quot; height=&quot;367&quot; data-origin-width=&quot;1811&quot; data-origin-height=&quot;367&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 시간 삽질을 했다 .. 쿼리가 잘 못 됬는지 확인해보고, 애노테이션도 확인해보고 삽질이란 삽질은 다 했다가&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Repository 자체가 문제란걸 깨닫고 CustomRepository 규칙을 다시 보았다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;기본 Impl으로 설정되어있는 접미사가 있어야 있어야 Bean을 만드는 규칙&lt;/span&gt;이 있었다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 리플렉션 api 같은 걸 써서 bean을 등록하고 돌아가기 때문.&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7rz6m/btryUfKr5zs/JusdG17ByLTGFjpmBYED31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7rz6m/btryUfKr5zs/JusdG17ByLTGFjpmBYED31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7rz6m/btryUfKr5zs/JusdG17ByLTGFjpmBYED31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7rz6m%2FbtryUfKr5zs%2FJusdG17ByLTGFjpmBYED31%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;985&quot; height=&quot;247&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;247&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;926&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csKb47/btryVaapIaJ/I1ed4Ouh5jwjKDU7YU8qy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csKb47/btryVaapIaJ/I1ed4Ouh5jwjKDU7YU8qy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csKb47/btryVaapIaJ/I1ed4Ouh5jwjKDU7YU8qy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsKb47%2FbtryVaapIaJ%2FI1ed4Ouh5jwjKDU7YU8qy0%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;926&quot; height=&quot;471&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;471&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;결론 : Impl 꼭 붙일것.. Custom에 목 매이지 말고 그냥 별도의 Repository 로 따로 빼서 빈으로 만드는 게 더 나을수도&lt;/b&gt;&lt;/p&gt;</description>
      <category>Backend/JPA</category>
      <category>JpaCustomRepository</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/125</guid>
      <comments>https://katastrophe.tistory.com/125#entry125comment</comments>
      <pubDate>Sun, 10 Apr 2022 12:53:34 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [1445] 일요일 아침의 데이트 JAVA</title>
      <link>https://katastrophe.tistory.com/124</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1445&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1445&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649404243982&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;1445번: 일요일 아침의 데이트&quot; data-og-description=&quot;첫째 줄에 숲의 세로 크기 N과 가로 크기 M이 주어진다. N과 M은 3보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 숲의 지도가 주어진다. 숲의 지도는 S, F, g, . 만으로 이루어져 있&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1445&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1445&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wld3C/hyNXwXXmtI/VmKktPa42T5BonUu4MyD3k/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1445&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1445&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wld3C/hyNXwXXmtI/VmKktPa42T5BonUu4MyD3k/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1445번: 일요일 아침의 데이트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 숲의 세로 크기 N과 가로 크기 M이 주어진다. N과 M은 3보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 숲의 지도가 주어진다. 숲의 지도는 S, F, g, . 만으로 이루어져 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b02rZq/btryMnP7xOY/8OuBVLZQbzWuEIYZr8QDe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b02rZq/btryMnP7xOY/8OuBVLZQbzWuEIYZr8QDe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b02rZq/btryMnP7xOY/8OuBVLZQbzWuEIYZr8QDe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb02rZq%2FbtryMnP7xOY%2F8OuBVLZQbzWuEIYZr8QDe0%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;1228&quot; height=&quot;789&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;789&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;1211&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UgoBu/btryM0UGHjz/ccSkNzWkZh9T6aXWiLP8q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UgoBu/btryM0UGHjz/ccSkNzWkZh9T6aXWiLP8q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UgoBu/btryM0UGHjz/ccSkNzWkZh9T6aXWiLP8q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUgoBu%2FbtryM0UGHjz%2FccSkNzWkZh9T6aXWiLP8q0%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;1211&quot; height=&quot;608&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;우선순위큐를 이용한 bfs 하는 문제이다. 우선시 되어야 하는 기준은&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 쓰레기를 지나지 않는 것&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 쓰레기 근처도 지나지 않는 것&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그런데 2 번 조건을 처리하기 위해 처음 입력때 받은 g 를 list 안에 넣고&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;list를 순회하면서 map의 값이 '.' =&amp;gt; 'm' 으로 바꾸고&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;bfs를 수행할 Queue 의 Comparator를 정해 주었다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;출발지를 push 한 다음 탐색하면 끝&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649404500273&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;

public class Main {

    static int[] dx = {0,0,1,-1},dy={1,-1,0,0};
    static char[][] map;
    static Point start;
    static int w,h;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt).toArray();
        h = input[0];w=input[1];
        map = new char[h][w];

        LinkedList&amp;lt;Point&amp;gt; gList = new LinkedList&amp;lt;&amp;gt;();
        for(int i=0;i&amp;lt;h;i++){
            map[i] = br.readLine().toCharArray();

            for(int j=0;j&amp;lt;w;j++){
                if(map[i][j]=='S') start = new Point(j, i,0,0);
                else if(map[i][j]=='g') gList.add(new Point(j,i));
            }
        } //end input

        setNearby(gList);
        Point result = bfs(start);
        System.out.println(result.gCnt+&quot; &quot;+result.mCnt);
    }
    static void setNearby(LinkedList&amp;lt;Point&amp;gt; gList){

        for (Point point : gList) {
            for(int i=0;i&amp;lt;dx.length;i++){
                int nextX = point.x+dx[i];
                int nextY = point.y+dy[i];

                if(isRange(nextX,nextY) &amp;amp;&amp;amp; map[nextY][nextX]=='.')
                    map[nextY][nextX]='m';
            }
        }
    }
    static Point bfs(Point start){
        Queue&amp;lt;Point&amp;gt; q = new PriorityQueue&amp;lt;&amp;gt;((o1, o2) -&amp;gt;
                o1.gCnt==o2.gCnt?o1.mCnt-o2.mCnt:o1.gCnt-o2.gCnt);
        boolean[][] visit = new boolean[h][w];

        q.add(start);
        visit[start.y][start.x]=true;
        while (!q.isEmpty()){

            Point cur = q.poll();

            if(map[cur.y][cur.x]=='F')
                return cur;

            for(int i=0;i&amp;lt;dx.length;i++){

                int nextX = cur.x+dx[i];
                int nextY = cur.y+dy[i];

                if(!isRange(nextX,nextY) || visit[nextY][nextX])
                    continue;

                switch (map[nextY][nextX]){
                    case 'g'-&amp;gt;q.add(new Point(nextX, nextY, cur.gCnt + 1, cur.mCnt));
                    case 'm'-&amp;gt;q.add(new Point(nextX, nextY, cur.gCnt, cur.mCnt + 1));
                    default -&amp;gt; q.add(new Point(nextX, nextY, cur.gCnt, cur.mCnt));
                }
                visit[nextY][nextX]=true;
            }
        }
        return null;
    }
    static boolean isRange(int x,int y){
        return x&amp;gt;=0&amp;amp;&amp;amp;y&amp;gt;=0&amp;amp;&amp;amp;y&amp;lt;h&amp;amp;&amp;amp;x&amp;lt;w;
    }
    static class Point{
        int x, y;
        int gCnt,mCnt;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public Point(int x, int y, int gCnt, int mCnt) {
            this.x = x;
            this.y = y;
            this.gCnt = gCnt;
            this.mCnt = mCnt;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>BFS</category>
      <category>백준 1445 java</category>
      <category>백준 일요일 아침의 데이트 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/124</guid>
      <comments>https://katastrophe.tistory.com/124#entry124comment</comments>
      <pubDate>Fri, 8 Apr 2022 16:55:28 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [2980] 도로와 신호등 JAVA</title>
      <link>https://katastrophe.tistory.com/123</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2980&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/2980&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649347042248&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;2980번: 도로와 신호등&quot; data-og-description=&quot;상근이는 트럭을 가지고 긴 일직선 도로를 운전하고 있다. 도로에는 신호등이 설치되어 있다. 상근이는 각 신호등에 대해서 빨간 불이 지속되는 시간과 초록 불이 지속되는 시간을 미리 구해왔&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2980&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2980&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QkvzE/hyNXkv3fM5/rulfKLQSrqZnDcbuK9QpL1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2980&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2980&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QkvzE/hyNXkv3fM5/rulfKLQSrqZnDcbuK9QpL1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;2980번: 도로와 신호등&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;상근이는 트럭을 가지고 긴 일직선 도로를 운전하고 있다. 도로에는 신호등이 설치되어 있다. 상근이는 각 신호등에 대해서 빨간 불이 지속되는 시간과 초록 불이 지속되는 시간을 미리 구해왔&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nTwhA/btryJu8IUm8/7H7iCO59Jkx8EhJFvjByu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nTwhA/btryJu8IUm8/7H7iCO59Jkx8EhJFvjByu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nTwhA/btryJu8IUm8/7H7iCO59Jkx8EhJFvjByu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnTwhA%2FbtryJu8IUm8%2F7H7iCO59Jkx8EhJFvjByu0%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;1090&quot; height=&quot;790&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;총 시간을 (빨간불+파란불) 로 % 연산을 하여 현재 신호등이 빨간불인지 파란불인지 구간을 파악해야한다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;구간을 section 이라고 했을 때 빨간불 지속시간 보다 작으면 =&amp;gt; 빨간불 구간&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;or =&amp;gt; 파란불 구간&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;기다리는 시간을 더해주고 다음위치로 이동할 때 이동거리 만큼 총 시간에 더해주는 식으로 하면 된다&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649572003028&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt).toArray();
        int n = input[0], len = input[1], curPos = 0, time = 0;
        
        for (int i = 0; i &amp;lt; n; i++) {
            int[] arr = stream(br.readLine().split(&quot; &quot;))
                    .mapToInt(Integer::parseInt).toArray();
            // arr = { 위치, 빨간불, 파란불}
            time += arr[0] - curPos; // 다음 위치 까지 가는데 걸리는 시간
            curPos = arr[0]; // 위치 이동
            
            int section = time % (arr[1] + arr[2]); // 신호등 사이클 확인
            if (section &amp;lt; arr[1]) // 빨간불 구간
                time += arr[1] - section;
        }
        // 남은 신호등 거리 계산 포함
        System.out.println(time + len - curPos);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSUc0m/btryMnUQDZI/bp9jhUEO1B5XTCOceieJKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSUc0m/btryMnUQDZI/bp9jhUEO1B5XTCOceieJKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSUc0m/btryMnUQDZI/bp9jhUEO1B5XTCOceieJKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSUc0m%2FbtryMnUQDZI%2Fbp9jhUEO1B5XTCOceieJKK%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;1034&quot; height=&quot;248&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 2980 java</category>
      <category>백준 도로와신호등 java</category>
      <category>시뮬레이션</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/123</guid>
      <comments>https://katastrophe.tistory.com/123#entry123comment</comments>
      <pubDate>Fri, 8 Apr 2022 01:00:57 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [8979] 올림픽 JAVA</title>
      <link>https://katastrophe.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/8979&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/8979&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649334093788&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;8979번: 올림픽&quot; data-og-description=&quot;입력의 첫 줄은 국가의 수 N(1 &amp;le; N &amp;le; 1,000)과 등수를 알고 싶은 국가 K(1 &amp;le; K &amp;le; N)가 빈칸을 사이에 두고 주어진다. 각 국가는 1부터 N 사이의 정수로 표현된다. 이후 N개의 각 줄에는 차례대로 각 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/8979&quot; data-og-url=&quot;https://www.acmicpc.net/problem/8979&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cUbQlT/hyNXtfgLCL/sH5bw8erJnfMxPfBqOwNiK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/8979&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/8979&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cUbQlT/hyNXtfgLCL/sH5bw8erJnfMxPfBqOwNiK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;8979번: 올림픽&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;입력의 첫 줄은 국가의 수 N(1 &amp;le; N &amp;le; 1,000)과 등수를 알고 싶은 국가 K(1 &amp;le; K &amp;le; N)가 빈칸을 사이에 두고 주어진다. 각 국가는 1부터 N 사이의 정수로 표현된다. 이후 N개의 각 줄에는 차례대로 각&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDcFM0/btryJXP8zc4/kcy6UtJycz1TTTAyB9X1tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDcFM0/btryJXP8zc4/kcy6UtJycz1TTTAyB9X1tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDcFM0/btryJXP8zc4/kcy6UtJycz1TTTAyB9X1tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDcFM0%2FbtryJXP8zc4%2Fkcy6UtJycz1TTTAyB9X1tk%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;1384&quot; height=&quot;807&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;807&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;1373&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FlJNm/btryLBS6NKM/D0PQM0qIzqcqvDca1H5jBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FlJNm/btryLBS6NKM/D0PQM0qIzqcqvDca1H5jBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FlJNm/btryLBS6NKM/D0PQM0qIzqcqvDca1H5jBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFlJNm%2FbtryLBS6NKM%2FD0PQM0qIzqcqvDca1H5jBk%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;1373&quot; height=&quot;678&quot; data-origin-width=&quot;1373&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주어진 메달의 갯수로 어떤 나라가 몇등 했는지 구하는 문제이다&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Medal 클래스를 만들고 비교연산을 위해 Comparable 인터페이스를 implements 한다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비교순위를 보장해주는 TreeMap &amp;lt;메달,나라들&amp;gt; 컬렉션을 사용하여 똑같은 점수가 있을 경우&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 점수의 value 를 List 안에 추가하는 방식으로 입력을 받는다&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런 다음 TreeMap을 순회하면서 구할 나라의 등수를 계산&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649571931539&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;

public class Main {


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
         int[] input = stream(br.readLine().split(&quot; &quot;))
                  .mapToInt(Integer::parseInt).toArray();

         int n = input[0]; // 나라 크기
         int query = input[1]; // 등수를 구할 나라

        // 정렬 기준 : 점수 내림차순 (key)
        TreeMap&amp;lt;Medal, ArrayList&amp;lt;Integer&amp;gt;&amp;gt; ranking = new TreeMap&amp;lt;&amp;gt;();

        for(int i=0;i&amp;lt;n;i++){
             int[] info = stream(br.readLine().split(&quot; &quot;))
                      .mapToInt(Integer::parseInt).toArray();
            Medal score = new Medal(info[1],info[2],info[3]);

            if(ranking.containsKey(score)){  // 해당 금,은,동 점수의 국가가 또 존재할 경우 추가
                ranking.get(score).add(info[0]);
            }else{
                ArrayList&amp;lt;Integer&amp;gt; nations = new ArrayList&amp;lt;&amp;gt;(); // 특정 금,은,동 스코어가 처음 나오면 Map에 추가
                nations.add(info[0]);
                ranking.put(score,nations);
            }
        }

        int ans=0; // 등수

        for (var entry : ranking.entrySet()) {
            ArrayList&amp;lt;Integer&amp;gt; nations = entry.getValue();

            if(nations.contains(query)){ // entry 를 탐색하던 도중 구하고 싶은 나라가 포함되어 있으면 등수 +1 하고 출력
                System.out.println(ans+1);
                break;
            }else ans+=nations.size(); // 나라들이 포함되어 있는 리스트 사이즈 만큼 등수 추가
        }
    }
    static class Medal implements Comparable&amp;lt;Medal&amp;gt;{
        int gold,silver,bronze;

        public Medal(int gold, int silver, int bronze) {
            this.gold = gold;
            this.silver = silver;
            this.bronze = bronze;
        }

        @Override
        public int compareTo(Medal o) {

            if(o.gold == this.gold){
                if(o.silver==this.silver)
                    return o.bronze-this.bronze;
                return o.silver-this.silver;
            }
            return o.gold-this.gold;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dooVwe/btryHF4fpIu/sPKYziME3V9QYvRC63KJkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dooVwe/btryHF4fpIu/sPKYziME3V9QYvRC63KJkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dooVwe/btryHF4fpIu/sPKYziME3V9QYvRC63KJkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdooVwe%2FbtryHF4fpIu%2FsPKYziME3V9QYvRC63KJkK%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;1316&quot; height=&quot;326&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>백준 8979 java</category>
      <category>백준 올림픽 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/122</guid>
      <comments>https://katastrophe.tistory.com/122#entry122comment</comments>
      <pubDate>Thu, 7 Apr 2022 21:26:50 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 백준 [13460] 구슬탈출2 JAVA</title>
      <link>https://katastrophe.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13460&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/13460&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649306924165&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;13460번: 구슬 탈출 2&quot; data-og-description=&quot;첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 &amp;le; N, M &amp;le; 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/13460&quot; data-og-url=&quot;https://www.acmicpc.net/problem/13460&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BBhmV/hyNXkWhAAv/n7ivTCmAgslkKG0S3K6OgK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13460&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/13460&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BBhmV/hyNXkWhAAv/n7ivTCmAgslkKG0S3K6OgK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;13460번: 구슬 탈출 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 &amp;le; N, M &amp;le; 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1383&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOgrI7/btryF7yTgbl/kmrQN7GqSJlrbaKajjQaGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOgrI7/btryF7yTgbl/kmrQN7GqSJlrbaKajjQaGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOgrI7/btryF7yTgbl/kmrQN7GqSJlrbaKajjQaGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOgrI7%2FbtryF7yTgbl%2FkmrQN7GqSJlrbaKajjQaGK%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;1383&quot; height=&quot;885&quot; data-origin-width=&quot;1383&quot; data-origin-height=&quot;885&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;1364&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmZYP/btryFnPug7t/KOuoX8w6acBo91Pz8u7W5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmZYP/btryFnPug7t/KOuoX8w6acBo91Pz8u7W5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmZYP/btryFnPug7t/KOuoX8w6acBo91Pz8u7W5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmmZYP%2FbtryFnPug7t%2FKOuoX8w6acBo91Pz8u7W5k%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;1364&quot; height=&quot;697&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; &lt;b&gt;풀이&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;구슬을 이동시키다가 빨간색 구슬만 구멍에 빠지는 최소 move 카운트를 return 하는 문제이다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;우선 최소 횟수는 좌표 BFS 특성상 먼저 끝내는 것이 최소 횟수 이므로 종결조건을 만족하기만 하면 return&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 총 4방향 {아래, 위, 오른, 왼} 으로 움직이는데 벽인 '#' 기호를 만날 때 까지 움직인다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 파란색 구슬의 좌표 nextB(X,Y) 가 구멍에 빠지면 continue&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;u&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2-1. 빨간색구슬 nextR(X,Y) 가 빠지면 move 카운트 return&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 두 개의 구슬 좌표가 &lt;span style=&quot;color: #ee2323;&quot;&gt;겹칠&lt;/span&gt; 경우 =&amp;gt; 움직인 방향에 따라서 &lt;span style=&quot;color: #ee2323;&quot;&gt;원래 구슬위치 기준으로 1 만큼 조정&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 조정 된 위치가 처음 방문하는 좌표이면 큐에 Push&amp;nbsp; &amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649307483906&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.io.*;


public class Main {

    static char[][] map;
    static int[] dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; // 아래, 위, 오른, 왼
    static int w, h;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] input = stream(br.readLine().split(&quot; &quot;))
                .mapToInt(Integer::parseInt).toArray();
        h = input[0];
        w = input[1];

        map = new char[h][w];
        Board board = new Board();

        for (int i = 0; i &amp;lt; h; i++) {
            map[i] = br.readLine().toCharArray();
            for (int j = 0; j &amp;lt; w; j++) {

                switch (map[i][j]) {
                    case 'R' -&amp;gt; board.red = (new Point(j, i));
                    case 'B' -&amp;gt; board.blue = (new Point(j, i));
                    case 'O' -&amp;gt; Board.hole = (new Point(j, i));
                }
            }
        }//end input

        int bfs = bfs(board);
        System.out.println(bfs);
    }

    static int bfs(Board board) {

        boolean[][][][] visit = new boolean[h][w][h][w];

        LinkedList&amp;lt;Board&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        q.add(board);
        visit[board.red.y][board.red.x][board.blue.y][board.blue.x] = true;

        while (!q.isEmpty()) {

            Board curBoard = q.poll();

            if (curBoard.move &amp;gt; 9) break; // 종결조건

            for (int i = 0; i &amp;lt; dx.length; i++) {

                int nextRX = curBoard.red.x;
                int nextRY = curBoard.red.y;
                int nextBX = curBoard.blue.x;
                int nextBY = curBoard.blue.y;

                while (isRange(nextRX + dx[i], nextRY + dy[i])) { // 빨간색 구슬 이동
                    nextRX += dx[i];
                    nextRY += dy[i];

                    if (nextRX == Board.hole.x &amp;amp;&amp;amp; nextRY == Board.hole.y)
                        break;
                }

                while (isRange(nextBX + dx[i], nextBY + dy[i])) { // 파란색 구슬 이동
                    nextBX += dx[i];
                    nextBY += dy[i];

                    if (nextBX == Board.hole.x &amp;amp;&amp;amp; nextBY == Board.hole.y)
                        break;
                }

                if (Board.hole.x == nextBX &amp;amp;&amp;amp; Board.hole.y == nextBY)
                    continue;
                if (Board.hole.x == nextRX &amp;amp;&amp;amp; Board.hole.y == nextRY) // 빨간색만 빠진경우 종료
                    return curBoard.move + 1;

                //dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
                if (nextRX == nextBX &amp;amp;&amp;amp; nextRY == nextBY) { // 위치가 겹칠 경우 조정

                    switch (i) {
                        case 0 -&amp;gt; {
                            if (curBoard.red.y &amp;gt; curBoard.blue.y) nextBY -= 1;
                            else nextRY -= 1;
                        }
                        case 1 -&amp;gt; {
                            if (curBoard.red.y &amp;lt; curBoard.blue.y) nextBY += 1;
                            else nextRY += 1;
                        }
                        case 2 -&amp;gt; {
                            if (curBoard.red.x &amp;gt; curBoard.blue.x) nextBX -= 1;
                            else nextRX -= 1;
                        }
                        case 3 -&amp;gt; {
                            if (curBoard.red.x &amp;lt; curBoard.blue.x) nextBX += 1;
                            else nextRX += 1;
                        }
                    }
                }

                if (!visit[nextRY][nextRX][nextBY][nextBX]) { // 각 구슬의 위치가 처음 기록되는 경우만 push
                    visit[nextRY][nextRX][nextBY][nextBX] = true;
                    q.add(new Board(new Point(nextBX, nextBY), new Point(nextRX, nextRY), curBoard.move + 1));
                }
            }
        }
        return -1;
    }

    static boolean isRange(int x, int y) {
        return x &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; w &amp;amp;&amp;amp; y &amp;lt; h &amp;amp;&amp;amp; map[y][x] != '#';
    }
    static class Point {
        int x, y;
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    static class Board {

        Point blue, red;
        static Point hole;
        int move;

        public Board() {
            move = 0;
        }

        public Board(Point blue, Point red, int move) {
            this.blue = blue;
            this.red = red;
            this.move = move;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2j3gB/btryI5GUFuD/fb1gKF21MBKKfmLa2QqcY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2j3gB/btryI5GUFuD/fb1gKF21MBKKfmLa2QqcY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2j3gB/btryI5GUFuD/fb1gKF21MBKKfmLa2QqcY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2j3gB%2FbtryI5GUFuD%2Ffb1gKF21MBKKfmLa2QqcY0%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;1300&quot; height=&quot;335&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>알고리즘,PS/백준</category>
      <category>BFS</category>
      <category>백준 13460 java</category>
      <category>백준 구슬탈출2 java</category>
      <author>김까따</author>
      <guid isPermaLink="true">https://katastrophe.tistory.com/121</guid>
      <comments>https://katastrophe.tistory.com/121#entry121comment</comments>
      <pubDate>Thu, 7 Apr 2022 13:58:30 +0900</pubDate>
    </item>
  </channel>
</rss>