<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dumbveloper.log</title>
        <link>https://velog.io/</link>
        <description>💪 점진적 과부하로 성장하는 개발자</description>
        <lastBuildDate>Fri, 09 Aug 2024 02:09:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dumbveloper.log</title>
            <url>https://velog.velcdn.com/images/dumbveloper_100/profile/4b9d989b-0830-462e-90c6-d7e5af153328/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dumbveloper.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dumbveloper_100" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[백준_1697_숨바꼭질]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%801697%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%801697%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88</guid>
            <pubDate>Fri, 09 Aug 2024 02:09:53 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1697">숨바꼭질</a></p>
<h2 id="풀이">풀이</h2>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int N,K,ans;
    static int[] map;
     public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());
        map = new int[100_001];

        dfs(N);
    }
    static void dfs(int n) {
        Queue&lt;Integer&gt; q = new ArrayDeque&lt;&gt;();
        q.add(n);

        map[n] = 1;

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

            if (now == K) {
                System.out.println(map[now] - 1); // 맨처음 시작
            }
            if (now - 1 &gt;= 0 &amp;&amp; map[now - 1] == 0) {
                map[now - 1] = map[now] + 1;
                q.add(now - 1);
            }
            if (now + 1 &lt;= 100000 &amp;&amp; map[now + 1] == 0) {
                map[now + 1] = map[now] + 1;
                q.add(now + 1);
            }
            if (2 * now &lt;= 100000 &amp;&amp; map[2 * now] == 0) {
                map[2 * now] = map[now] + 1;
                q.add(2 * now);
            }

        }
    }

}</code></pre>
<p>BFS는 레벨별로 탐색을 진행하기 때문에 최단 경로 문제에 적합하다~!</p>
<pre><code class="language-java">map[n] = 1;
...
System.out.println(map[now] - 1);</code></pre>
<ul>
<li>시작점이 같을때도 한번에 처리 가능!</li>
</ul>
<h4 id="처음엔-dfs로-풀었는데-시간초과-발생">처음엔 dfs로 풀었는데 &quot;시간초과&quot; 발생!</h4>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main2 {
    static int N, K, ans;
    static boolean[] visited;

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

        N = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());
        visited = new boolean[100_001];
        ans = Integer.MAX_VALUE;

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

    static void dfs(int n, int cnt) {
        if (n &lt; 0 || n &gt; 100000 || visited[n]) {
            return;
        }

        if (n == K) {
            ans = Math.min(ans, cnt);
            return;
        }

        visited[n] = true;

        dfs(n - 1, cnt + 1);
        dfs(n + 1, cnt + 1);
        dfs(2 * n, cnt + 1);

        visited[n] = false;
    }
}
</code></pre>
<p>위풀이는 너무 많은 재귀를 하게 됨!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_14503_로봇청소기]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%8014503%EB%A1%9C%EB%B4%87%EC%B2%AD%EC%86%8C%EA%B8%B0</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%8014503%EB%A1%9C%EB%B4%87%EC%B2%AD%EC%86%8C%EA%B8%B0</guid>
            <pubDate>Thu, 08 Aug 2024 02:20:20 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/14503">로봇 청소기</a></p>
<h2 id="첫-풀이">첫 풀이</h2>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int N, M, r, c, d, ans;
    static int[] dx = {-1, 0, 1, 0};
    static int[] dy = {0, 1, 0, -1};
    static int[][] map;
    static boolean[][] cleaned;
    static class Robot{
        int x,y,d;
        public Robot(int x, int y, int d){
            this.x = x;
            this.y = y;
            this.d = d;
        }
        private void turnLeft(){
            this.d = (this.d + 3) % 4;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        map = new int[N][M];
        cleaned = new boolean[N][M];
        st = new StringTokenizer(br.readLine());
        r = Integer.parseInt(st.nextToken());
        c = Integer.parseInt(st.nextToken());
        d = Integer.parseInt(st.nextToken());
        Robot robot = new Robot(r, c, d);

        for(int i = 0; i &lt; N; i++){
            st = new StringTokenizer(br.readLine());
            for(int j = 0; j &lt; M; j++){
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        while(true){
            if(!cleaned[robot.x][robot.y]){
                cleaned[robot.x][robot.y] = true;
                ans++;
            }

            boolean moved = false;

            for(int i = 0; i &lt; 4; i++){
                int nx = robot.x + dx[i];
                int ny = robot.y + dy[i];

                if(nx &gt;= 0 &amp;&amp; nx &lt; N &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; M &amp;&amp; !cleaned[nx][ny] &amp;&amp; map[nx][ny] == 0){
                    moved = true;
                    break;
                }
            }

            if(moved){
                robot.turnLeft();
                int nx = robot.x + dx[robot.d];
                int ny = robot.y + dy[robot.d];
                if(nx &gt;= 0 &amp;&amp; nx &lt; N &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; M &amp;&amp; !cleaned[nx][ny] &amp;&amp; map[nx][ny] == 0){
                    robot.x = nx;
                    robot.y = ny;
                }
            }
            if(!moved){
                int backDir = (robot.d + 2) % 4;
                int nx = robot.x + dx[backDir];
                int ny = robot.y + dy[backDir];

                if(nx &gt;= 0 &amp;&amp; nx &lt; N &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; M &amp;&amp; map[nx][ny] != 1){
                    robot.x = nx;
                    robot.y = ny;
                    continue;
                }
                break;
            }

        }

        System.out.print(ans);
    }
}</code></pre>
<p>움직임 가능 처리 조건문의 오류</p>
<pre><code class="language-java">if(nx &gt;= 0 &amp;&amp; nx &lt; N &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; M &amp;&amp; !cleaned[nx][ny])</code></pre>
<p>벽도 청소가 안된 상태라는 것을 깜빡하고 조건문을 잘못 짰었다!
청소가 되었어도 후진은 할수 있다!</p>
<h2 id="개선">개선</h2>
<blockquote>
<p>어짜피 주변의 청소되지 않은 칸으로 이동하는 것이라면 🤔
for(int i = 0; i &lt; 4; i++)으로 순으로 dx[i], dy[i] 를 찾는 것이 아닌 turnLeft 메소드로 이동가능 여부를 판단해도 될듯하다!
👉 어짜피 움직일 칸이 없으면 다시 원래 방향을 바라보게 된다! (4번 반시계 회전)</p>
</blockquote>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int N, M, r, c, d, ans;
    static int[] dx = {-1, 0, 1, 0};
    static int[] dy = {0, 1, 0, -1};
    static int[][] map;
    static boolean[][] cleaned;
    static class Robot{
        int x,y,d;
        public Robot(int x, int y, int d){
            this.x = x;
            this.y = y;
            this.d = d;
        }
        private void turnLeft(){
            this.d = (this.d + 3) % 4;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        map = new int[N][M];
        cleaned = new boolean[N][M];
        st = new StringTokenizer(br.readLine());
        r = Integer.parseInt(st.nextToken());
        c = Integer.parseInt(st.nextToken());
        d = Integer.parseInt(st.nextToken());
        Robot robot = new Robot(r, c, d);

        for(int i = 0; i &lt; N; i++){
            st = new StringTokenizer(br.readLine());
            for(int j = 0; j &lt; M; j++){
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        while(true){
            if(!cleaned[robot.x][robot.y]){
                cleaned[robot.x][robot.y] = true;
                ans++;
            }

            boolean moved = false;

            for(int i = 0; i &lt; 4; i++){
                robot.turnLeft();
                int nx = robot.x + dx[robot.d];
                int ny = robot.y + dy[robot.d];

                if(nx &gt;= 0 &amp;&amp; nx &lt; N &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; M &amp;&amp; !cleaned[nx][ny] &amp;&amp; map[nx][ny] == 0){
                    moved = true;
                    robot.x = nx;
                    robot.y = ny;
                    break;
                }
            }

            if(!moved){
                int backDir = (robot.d + 2) % 4;
                int nx = robot.x + dx[backDir];
                int ny = robot.y + dy[backDir];

                if (nx &lt; 0 || ny &lt; 0 || nx &gt;= N || ny &gt;= M || map[nx][ny] == 1) {
                    break;
                }
                robot.x = nx;
                robot.y = ny;
            }
        }
        System.out.print(ans);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_타켓넘버]]></title>
            <link>https://velog.io/@dumbveloper_100/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%ED%83%80%EC%BC%93%EB%84%98%EB%B2%84</link>
            <guid>https://velog.io/@dumbveloper_100/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%ED%83%80%EC%BC%93%EB%84%98%EB%B2%84</guid>
            <pubDate>Tue, 06 Aug 2024 14:35:05 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43165">타켓 넘버</a></p>
<h2 id="풀이">풀이</h2>
<pre><code class="language-java">class Solution {
    static int[] numbers;
    static int N;
    static int target;
    static int count = 0;

    public int solution(int[] numbers, int target) {
        Solution.numbers = numbers;
        N = numbers.length;
        Solution.target = target;
        dfs(0, 0);
        return count;
    }

    static void dfs(int idx, int sum) {
        if (idx == N) {
            if (sum == target) {
                count++;
            }
            return;
        }

        dfs(idx + 1, sum + numbers[idx]);

        dfs(idx + 1, sum - numbers[idx]);
    }
}</code></pre>
<p>static 변수를 두고 푸는게 편하게 느껴진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_가장큰수]]></title>
            <link>https://velog.io/@dumbveloper_100/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B0%80%EC%9E%A5%ED%81%B0%EC%88%98</link>
            <guid>https://velog.io/@dumbveloper_100/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B0%80%EC%9E%A5%ED%81%B0%EC%88%98</guid>
            <pubDate>Tue, 06 Aug 2024 13:12:44 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42746">가장 큰 수</a></p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public String solution(int[] numbers) {
        // int 배열을 String 배열로 변환
        String[] strNumbers = new String[numbers.length];
        for (int i = 0; i &lt; numbers.length; i++) {
            strNumbers[i] = String.valueOf(numbers[i]);
        }

        Arrays.sort(strNumbers, (s1, s2) -&gt; (s2 + s1).compareTo(s1 + s2));

        if (strNumbers[0].equals(&quot;0&quot;)) {
            return &quot;0&quot;;
        }

        StringBuilder sb = new StringBuilder();
        for (String str : strNumbers) {
            sb.append(str);
        }

        return sb.toString();
    }
}</code></pre>
<h3 id="👉-정렬">👉 정렬</h3>
<pre><code class="language-java">Arrays.sort(strNumbers, (s1, s2) -&gt; (s2 + s1).compareTo(s1 + s2));
</code></pre>
<p>s1 = &quot;3&quot;, s2 = &quot;30&quot;:
s2 + s1 = &quot;303&quot;
s1 + s2 = &quot;330&quot;
(&quot;303&quot;).compareTo(&quot;330&quot;)은 -1을 반환합니다. 
👉 즉, &quot;30&quot;이 &quot;3&quot;보다 앞에 위치해야 합니다.
쉽게 말해서 더 큰쪽으로 정렬된다! </p>
<pre><code class="language-java">(s2 + s1).compareTo(s1 + s2): 더 큰 쪽을 앞에 배치 (내림차순 정렬) 이 문제
(s1 + s2).compareTo(s2 + s1): 더 작은 쪽을 앞에 배치 (오름차순 정렬)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_더맵게]]></title>
            <link>https://velog.io/@dumbveloper_100/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%8D%94%EB%A7%B5%EA%B2%8C</link>
            <guid>https://velog.io/@dumbveloper_100/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%8D%94%EB%A7%B5%EA%B2%8C</guid>
            <pubDate>Tue, 06 Aug 2024 11:22:08 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42626">더 맵게</a></p>
<h3 id="오답-풀이">오답 풀이</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();
        int N = scoville.length;

        for(int i = 0; i &lt; N; i++){
            pq.offer(scoville[i]);
        }

        while(pq.size() &gt;= 2){
            answer++;
            int sum = 0;
            int a = pq.poll();
            int b = pq.poll();
            sum = a + 2*b;
            pq.offer(sum);

            if(pq.peek() &gt;= K){
                return answer;
            }
        }

        if(pq.peek() &gt;= K){
            return answer;
        }else{
            return -1;
        }


    }
}</code></pre>
<ul>
<li>이렇게 풀면 처음부터 전부가 K 이상일 경우를 정확하게 잡아내지 못함!</li>
<li>무조건 섞고 판단하게 된다!</li>
</ul>
<h4 id="📌-처음에-섞지-않고도-만족하는-경우를-고려하지-못했다">📌 처음에 섞지 않고도 만족하는 경우를 고려하지 못했다!</h4>
<ul>
<li>조건문을 좀 더 심도있게 세우자</li>
</ul>
<h3 id="재풀이">재풀이</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();

        for (int scov : scoville) {
            pq.offer(scov);
        }

        while (pq.size() &gt; 1 &amp;&amp; pq.peek() &lt; K) {
            int a = pq.poll();
            int b = pq.poll();
            int newScoville = a + 2 * b;
            pq.offer(newScoville);
            answer++;
        }

        if (pq.peek() &lt; K) {
            return -1;
        } else {
            return answer;
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_1495_기타리스트]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%801495%EA%B8%B0%ED%83%80%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%801495%EA%B8%B0%ED%83%80%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Fri, 28 Jun 2024 08:57:21 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1495">기타리스트</a></p>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

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

        N = Integer.parseInt(st.nextToken()); // 곡의 갯수
        S = Integer.parseInt(st.nextToken()); // 처음 볼륨
        M = Integer.parseInt(st.nextToken()); // 최대 볼륨

        st = new StringTokenizer(br.readLine());

        // 각 곡의 볼륨 변화 (N+1 크기로 선언)
        int[] V = new int[N + 1];

        for (int i = 1; i &lt;= N; i++) {  // i는 1부터 시작
            V[i] = Integer.parseInt(st.nextToken());
        }

        // dp[i][j] : i번째 곡까지 연주했을 때 볼륨이 j인 경우 가능 여부 (true: 가능, false: 불가능)
        boolean[][] dp = new boolean[N + 1][M + 1];
        dp[0][S] = true; // 초기 볼륨으로 시작

        // 이전 값을 보고 현재 가능한 경우의 수 처리!
        for (int i = 1; i &lt;= N; i++) {
            for (int j = 0; j &lt;= M; j++) {
                if (dp[i - 1][j]) {
                    if (j + V[i] &lt;= M) {
                        dp[i][j + V[i]] = true;
                    }
                    if (j - V[i] &gt;= 0) {
                        dp[i][j - V[i]] = true;
                    }
                }
            }
        }

        // 마지막 곡까지 연주했을 때 가능한 최대 볼륨 찾기
        int maxVolume = -1;
        for (int i = M; i &gt;= 0; i--) {
            if (dp[N][i]) {
                maxVolume = i;
                break;
            }
        }
        System.out.println(maxVolume);
    }
}</code></pre>
<pre><code>N = 3 (곡의 개수)
S = 5 (시작 볼륨)
M = 10 (최대 볼륨)
볼륨 변화량 = [5, 3, 7]</code></pre><p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/ca6add44-8892-4e5e-9ac4-12a2fa0e0074/image.png" alt=""></p>
<h3 id="dp-테이블을-채우는-방식-차이">dp 테이블을 채우는 방식 차이</h3>
<pre><code class="language-java"> for (int i = 1; i &lt;= N; i++) {
            for (int j = 0; j &lt;= M; j++) {
                if (dp[i - 1][j]) {
                    if (j + V[i] &lt;= M) {
                        dp[i][j + V[i]] = true;
                    }
                    if (j - V[i] &gt;= 0) {
                        dp[i][j - V[i]] = true;
                    }
                }
            }
        }</code></pre>
<p>이전 곡(i-1)에서 현재 볼륨 j를 만들 수 있는지(dp[i-1][j])를 확인하고, 현재 곡(i)에서 볼륨 변화량 V[i]를 더하거나 빼서 새로운 볼륨을 만듬</p>
<ul>
<li>이 방식은 이전 상태(i-1)에서 현재 상태(i)로 볼륨을 업데이트하는 접근 방식</li>
</ul>
<pre><code class="language-java">for (int i = 1; i &lt;= N; i++) {
            for (int j = 0; j &lt;= M; j++) {
                // 볼륨 줄이기 (-)
                if (j - V[i] &gt;= 0 &amp;&amp; dp[i - 1][j - V[i]]) {
                    dp[i][j] = true;
                }
                // 볼륨 올리기 (-)
                if (j + V[i] &lt;= M &amp;&amp; dp[i - 1][j + V[i]]) {
                    dp[i][j] = true;
                }
            }
        }
</code></pre>
<p>현재 볼륨 j를 만들기 위해 이전 곡(i-1)에서 가능한 모든 경우를 체크</p>
<ul>
<li>이 방식은 현재 상태(i)에서 이전 상태(i-1)를 참고하여 볼륨을 업데이트하는 접근 방식</li>
</ul>
<h4 id="차이점">차이점</h4>
<p>업데이트 시점</p>
<ul>
<li>첫 번째 방식: 이전 상태(i-1)에서 가능한 모든 볼륨을 확인하고, 현재 상태(i)의 볼륨을 업데이트</li>
<li>두 번째 방식: 현재 상태(i)의 볼륨을 만들기 위해 이전 상태(i-1)에서 가능한 모든 경우를 체크</li>
</ul>
<p>루프 구조</p>
<ul>
<li>첫 번째 방식: 이전 상태의 볼륨을 기반으로 현재 상태의 새로운 볼륨을 직접 업데이트.</li>
<li>두 번째 방식: 현재 상태의 볼륨을 만들기 위해 이전 상태의 볼륨을 역으로 추적하여 업데이트.</li>
</ul>
<h4 id="🤔-뭐가-더-좋을까">🤔 뭐가 더 좋을까?</h4>
<ul>
<li><p>첫 번째 방식
내부 루프에서 조건문을 통해 볼륨을 더하고 빼는 경우를 따로 체크
dp[i - 1][j]가 true인 경우에만 업데이트하므로, 불필요한 연산이 줄어듬</p>
</li>
<li><p>두 번째 방식
모든 볼륨 범위에서 이전 곡의 볼륨을 줄이거나 늘리는 경우를 체크
모든 경우를 다 체크하므로, 경우에 따라 더 많은 연산이 필요할 수 있음</p>
</li>
</ul>
<p>👉 첫 번째 방식이 더 나은 선택일 가능성이 크다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_9465_스티커]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%809465%EC%8A%A4%ED%8B%B0%EC%BB%A4</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%809465%EC%8A%A4%ED%8B%B0%EC%BB%A4</guid>
            <pubDate>Thu, 27 Jun 2024 17:06:14 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/9465">스티커</a></p>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        for (int t = 0; t &lt; T; t++) {
            int N = Integer.parseInt(br.readLine()); // 스티커 개수
            int[][] sticker = new int[2][N + 1]; // 스티커 정보 저장 (0: 첫 번째 줄, 1: 두 번째 줄)

            // 스티커 정보 입력
            for (int i = 0; i &lt; 2; i++) {
                StringTokenizer st = new StringTokenizer(br.readLine());
                for (int j = 1; j &lt;= N; j++) {
                    sticker[i][j] = Integer.parseInt(st.nextToken());
                }
            }

            // DP 테이블 초기화
            int[][] dp = new int[2][N + 1];
            dp[0][1] = sticker[0][1];
            dp[1][1] = sticker[1][1];

            // DP 계산
            for (int i = 2; i &lt;= N; i++) {
                dp[0][i] = Math.max(dp[1][i - 1], dp[1][i - 2]) + sticker[0][i];
                dp[1][i] = Math.max(dp[0][i - 1], dp[0][i - 2]) + sticker[1][i];
            }

            // 최대 점수 출력
            System.out.println(Math.max(dp[0][N], dp[1][N]));
        }
    }
}</code></pre>
<h3 id="dp-배열의-선택-기준">DP 배열의 선택 기준</h3>
<p>각 위치에서 가능한 선택 : &quot;현재 위치의 대각선에 있는 값&quot;과 &quot;대각선 왼쪽에 있는 값&quot;</p>
<h4 id="1-대각선-위치-i-1">1. 대각선 위치 (i-1)</h4>
<p>현재 위치의 스티커를 선택하면 바로 위/아래의 스티커는 선택할 수 없기 때문에 대각선 위치에 있는 스티커 값을 참고!</p>
<pre><code>50 10 100 20 40
30 50 70 10 60

예를 들어 dp[0][i]를 계산할 때 dp[1][i-1]를 참고하는 이유는
sticker[1][i-1]은 sticker[0][i]와 인접하지 않기 때문</code></pre><h4 id="2-대각선의-왼쪽-위치-i-2">2. 대각선의 왼쪽 위치 (i-2)</h4>
<p>dp[1][i-2] 같은 경우도 인접하지 않은 스티커</p>
<pre><code>50 10 100 20 40
30 50 70 10 60

dp[0][i]를 계산할 때 dp[1][i-2]를 참고하는 이유는 
더 먼 거리에 있는 스티커를 선택함으로써 인접하지 않은 최대 점수를 얻을 수 있기 때문</code></pre><h4 id="🤔-왜-같은-줄에서-두-칸-떨어진-위치를-참고-x">🤔 왜 같은 줄에서 두 칸 떨어진 위치를 참고 X?</h4>
<p>👉 이전에 선택된 스티커와의 인접성
중간에 있는 스티커가 무시될 위험이 있음!</p>
<pre><code>50 10 100 20 40
30 50 70 10 60

sticker[0][2]를 선택하고 sticker[0][4]를 선택하면
중간에 있는 sticker[0][3]을 고려하지 않게 되니까 인접한 스티커들을 제대로 체크하지 못함</code></pre><p>즉 선택기준은
👉 대각선에 있는 친구가 선택되었을때 vs 선택 안되었을때!
dp[0][i]를 계산할 때 sticker[1][i-1]가 선택되었을 때와 선택되지 않았을 때의 점수를 비교하는 방식!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_1149_RGB거리 ]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%801149RGB%EA%B1%B0%EB%A6%AC</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%801149RGB%EA%B1%B0%EB%A6%AC</guid>
            <pubDate>Thu, 27 Jun 2024 14:21:25 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1149">RGB거리 문제</a></p>
<p>DP를 활용한 문제</p>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class Main {

    final static int Red = 0;
    final static int Green = 1;
    final static int Blue = 2;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        int N = Integer.parseInt(br.readLine());

        int[][] dp = new int[N + 1][3];

        for (int i = 1; i &lt;= N; i++) {
            st = new StringTokenizer(br.readLine());

            int r = Integer.parseInt(st.nextToken());
            int g = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            dp[i][Red] = Math.min(dp[i - 1][Green], dp[i - 1][Blue]) + r;
            dp[i][Green] = Math.min(dp[i - 1][Red], dp[i - 1][Blue]) + g;
            dp[i][Blue] = Math.min(dp[i - 1][Red], dp[i - 1][Green]) + b;
        }

        System.out.println(Math.min(dp[N][0], Math.min(dp[N][1], dp[N][2])));

    }

}</code></pre>
<h4 id="1-dp-테이블-초기화">1. DP 테이블 초기화</h4>
<p>dp는 2차원 배열로, dp[i][j]는 i번째 집을 j 색깔로 칠했을 때의 최소 비용을 나타냄!</p>
<h4 id="2-dp-계산">2. DP 계산</h4>
<p>i번째 집을 칠할 때, 인접한 집의 색상을 고려하여 최소 비용을 계산</p>
<ul>
<li>i번째 집을 빨간색으로 칠할 때, i-1번째 집은 초록색 또는 파란색으로 칠해야 함</li>
<li>dp[i][Red]는 dp[i-1][Green]과 dp[i-1][Blue] 중 작은 값에 현재 집의 빨간색 비용을 더한 값
👉 현재 Red 비용값 + Math.min( 이전 누적 Green 비용값 ,이전 누적 Blue 비용값 )<pre><code class="language-java">dp[i][Red] = Math.min(dp[i - 1][Green], dp[i - 1][Blue]) + r;</code></pre>
</li>
</ul>
<p>이와 같은 방식으로 초록색과 파란색 비용도 계산!</p>
<h4 id="3-최소-비용-출력">3. 최소 비용 출력</h4>
<p>마지막 집(N번째 집)을 빨간색, 초록색, 파란색으로 칠했을 때의 최소 비용 중 가장 작은 값을 출력!</p>
<pre><code class="language-java">Math.min(dp[N][0], Math.min(dp[N][1], dp[N][2]))</code></pre>
<h4 id="2번째-풀이">2번째 풀이</h4>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    static int N;
    static final int RED = 0;
    static final int GREEN = 1;
    static final int BLUE = 2;

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

        N = Integer.parseInt(br.readLine()); // 집
        int[] R = new int[N + 1];
        int[] G = new int[N + 1];
        int[] B = new int[N + 1];

        for(int i = 1; i &lt;= N; i++){
            StringTokenizer st = new StringTokenizer(br.readLine());
            R[i] = Integer.parseInt(st.nextToken());
            G[i] = Integer.parseInt(st.nextToken());
            B[i] = Integer.parseInt(st.nextToken());
        }

        int[][] dp = new int[N + 1][3];

        // 1번 집 초기화
        dp[1][RED] = R[1];
        dp[1][GREEN] = G[1];
        dp[1][BLUE] = B[1];

        for (int i = 2; i &lt;= N; i++) {
            for (int j = 0; j &lt; 3; j++) {
                if (j == RED) {
                    dp[i][j] = Math.min(dp[i - 1][GREEN], dp[i - 1][BLUE]) + R[i];
                }
                if (j == GREEN) {
                    dp[i][j] = Math.min(dp[i - 1][RED], dp[i - 1][BLUE]) + G[i];
                }
                if (j == BLUE) {
                    dp[i][j] = Math.min(dp[i - 1][RED], dp[i - 1][GREEN]) + B[i];
                }
            }
        }

        /**
        for (int i = 2; i &lt;= N; i++) {
            // 지금 탐색하는 집이 해당 색으로 칠해질때의 최소 비용 계산
            dp[i][RED] = Math.min(dp[i - 1][GREEN], dp[i - 1][BLUE]) + R[i];
            dp[i][GREEN] = Math.min(dp[i - 1][RED], dp[i - 1][BLUE]) + G[i];
            dp[i][BLUE] = Math.min(dp[i - 1][RED], dp[i - 1][GREEN]) + B[i];
        }
        */

        int ans = Math.min(dp[N][BLUE], Math.min(dp[N][RED], dp[N][GREEN]));

        System.out.print(ans);

    }
}</code></pre>
<h4 id="만약-여기서">만약 여기서</h4>
<p>R, G, B .... 배열의 종류가 많아진다면 cost 2차원 배열을 두고 계산하는 것이 낫곘다!</p>
<pre><code class="language-java">int[][] cost = new int[N + 1][3]; // cost 

        for(int i = 1; i &lt;= N; i++){
            StringTokenizer st = new StringTokenizer(br.readLine());
            cost[i][RED] = Integer.parseInt(st.nextToken());
            cost[i][GREEN] = Integer.parseInt(st.nextToken());
            cost[i][BLUE] = Integer.parseInt(st.nextToken());
        }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌱 Spring의 ApplictionContext 과 DI]]></title>
            <link>https://velog.io/@dumbveloper_100/Spring%EC%9D%98-ApplictionContext</link>
            <guid>https://velog.io/@dumbveloper_100/Spring%EC%9D%98-ApplictionContext</guid>
            <pubDate>Wed, 24 Apr 2024 00:23:23 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-스프링-빈bean-이란">📌 스프링 빈(Bean) 이란?</h2>
<p>스프링 컨테이너가 관리하는 자바 객체를 의미합니다.
빈은 인스턴스화된 객체를 의미하며, 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 합니다.</p>
<pre><code class="language-java">&lt;bean id=&quot;hello&quot; class=&quot;com.example.Hello&quot;/&gt;
이렇게 xml 파일 빈 태그를 추가하면 hello 인스턴스를 스프링 컨테이너가 관리하게 됩니다.

Hello hello = new Hello()
즉 new 키워드 대신 사용하는 것!</code></pre>
<h2 id="🧐-applicationcontext-이란">🧐 ApplicationContext 이란?</h2>
<p>ApplicationContext는 Spring Container의 구현체 중 하나로, <strong>객체의 생성과 소멸 라이프사이클을 관리합니다.</strong> 즉 애플리케이션의 빈(Bean)들을 관리하고 필요한 객체를 제공하는 역할을 합니다.</p>
<ol>
<li>객체의 생성과 소멸 라이프사이클 관리: ApplicationContext는 설정된 빈의 라이프사이클을 관리합니다. 이를 통해 개발자는 빈의 라이프사이클에 관여하지 않고도 안정적으로 객체를 사용할 수 있습니다.</li>
<li>Configuration Metadata 필요: ApplicationContext를 구성하는 빈들은 Spring의 설정 문서 또는 Configuration Metadata를 필요로 합니다. 이는 XML 파일이나 JavaConfig 같은 설정 방법을 통해 정의될 수 있습니다. 이 설정 문서에는 애플리케이션에서 사용될 빈의 정보, 의존성, 빈의 스코프 등이 정의됩니다.</li>
<li>미리 객체를 만들어 놓는다 (사전 초기화): ApplicationContext는 빈을 미리 생성하고 필요할 때 제공하는 방식으로 동작합니다. 따라서 ApplicationContext가 로드될 때 설정된 빈들은 미리 생성되어 초기화됩니다. 이를 통해 애플리케이션이 시작될 때 필요한 객체들이 준비되어 있어야 할 때 속도와 효율성이 향상됩니다.</li>
<li>Scope 설정: ApplicationContext를 통해 생성되는 빈들은 기본적으로 싱글톤(Singleton) 스코프를 갖습니다. 이는 ApplicationContext가 단일 인스턴스를 생성하고 해당 인스턴스를 모든 요청에 대해 공유하는 것을 의미합니다. 또한, Prototype 스코프를 설정할 수도 있으며, 이는 각 요청에 대해 새로운 인스턴스를 생성하여 제공합니다. 이를 통해 빈의 스코프를 설정하여 객체의 생명주기를 관리할 수 있습니다.</li>
</ol>
<h2 id="🧪--test-1--빈의-스코프-singleton-vs-prototype">🧪  Test 1 : 빈의 스코프 &quot;singleton&quot; vs &quot;prototype&quot;</h2>
<pre><code class="language-java">public interface MessageBean {
    void sayHello(String name);
}


1. MessageBeanEnImpl
public class MessageBeanEnImpl implements MessageBean{
    public MessageBeanEnImpl() {
        System.out.println(&quot;MessageBeanEnImpl 생성자 호출됨&quot;);
    }
    public void sayHello(String name) {
        System.out.println(&quot;nice to meet you!&quot; + name);
    }

}

2. MessageBeanKoImpl
public class MessageBeanKoImpl implements MessageBean{
    public MessageBeanKoImpl() {
        System.out.println(&quot;MessageBeanKoImpl 생성자 호출됨&quot;);
    }
    public void sayHello(String name) {
        System.out.println(name + &quot; 님 방가방가&quot;);
    }
}

</code></pre>
<p>기존 java의 생성 방식</p>
<pre><code class="language-java">MessageBean bean = new MessageBeanEnImpl();
bean.sayHello(&quot;Bong&quot;);

bean = new MessageBeanKoImpl();
bean.sayHello(&quot;봉&quot;);</code></pre>
<h4 id="applicationcontextyml">applicationContext.yml</h4>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;bean class=&quot;sample01.MessageBeanKoImpl&quot; id=&quot;ko&quot; scope=&quot;singleton&quot;&gt;&lt;/bean&gt;
      &lt;bean class=&quot;sample01.MessageBeanKoImpl&quot; id=&quot;ko2&quot;/&gt;
    &lt;bean class=&quot;sample01.MessageBeanEnImpl&quot; id=&quot;en&quot;/&gt;

&lt;/beans&gt;</code></pre>
<ul>
<li><p>Bean 하나당 한개의 객체 생성 ( 기본값이 singleton )</p>
</li>
<li><p>scope = &quot;prototype&quot; 으로 하면 각 다른 객체 만들어집니다. </p>
<ul>
<li>미리 생성을 하지않고 지연초기화(지연로딩) </li>
<li>getBean() 요청할때 마다 만들기 때문에 각 새로운 객체 만들어 지게 됩니다.</li>
</ul>
</li>
<li><p>ko 와 ko2는 각각 빈으로 등록된것!</p>
</li>
</ul>
<pre><code class="language-java">public class MainTest {
    public static void main(String[] args) {

        //스프링 컨테이너는 설정문서 기반으로 만들어지는것 ! (classPath를 기준 경로)
        ApplicationContext context =
                new ClassPathXmlApplicationContext(&quot;applicationContext.xml&quot;);

        MessageBean bean = context.getBean(&quot;en&quot;, MessageBean.class); // context.getBean(String Id, class)
        bean.sayHello(&quot;Bong&quot;);

        bean = context.getBean(&quot;ko&quot;, MessageBean.class);
        bean.sayHello(&quot;봉&quot;);

        //getBean 할때마다 새롭게 만드나?? 아니면 만든거 사용하는건가?? - 동일한 객체!
        MessageBean bean2 = context.getBean(&quot;ko&quot;, MessageBean.class); // id가 동일하게 getBean()

        System.out.println(&quot;bean = &quot; + bean);
        System.out.println(&quot;bean2 = &quot; + bean2);
        //주소값이 같음! 즉, 싱글톤으로 관리되는 객체와 같음 (static class)
        //사전초기화 , 미리 객체를 만들어 놓음 !
        //bean 하나당 1객체

    }
}

- 만약 scope=&quot;prototype&quot; 라면 -&gt;  prototype 으로 하면 각 다른 객체 만들어짐! 즉 미리 생성을 안함! 지연초기화(지연로딩)
 * MessageBeanEnImpl 생성자 호출됨 - En은  singleton
 * MessageBeanKoImpl 생성자 호출됨 - 필요할때 생성
 * nice to meet you!Bong
 * MessageBeanKoImpl 생성자 호출됨 - 필요할때 생성
 * 봉 님 방가방가
 * MessageBeanKoImpl 생성자 호출됨 - Ko 하나더 생성
 * bean = sample01.MessageBeanKoImpl@23348b5d
 * bean2 = sample01.MessageBeanKoImpl@70325e14

 - 만약 scope = &quot;singleton&quot; 이면 -&gt; 미리 객체를 만들어 놓음 (기본값)
 * MessageBeanKoImpl 생성자 호출됨
 * MessageBeanEnImpl 생성자 호출됨
 * MessageBeanKoImpl 생성자 호출됨 - 미리 생성
 * nice to meet you!Bong
 * 봉 님 방가방가
 * bean = sample01.MessageBeanKoImpl@2df3b89c
 * bean2 = sample01.MessageBeanKoImpl@2df3b89c</code></pre>
<h2 id="🧪--test-2--여러-생성자-테스트">🧪  Test 2 : 여러 생성자 테스트</h2>
<h4 id="memberjava">Member.java</h4>
<pre><code class="language-java">@ToString
public class Member {
    private String id;
    private String pwd;
    private int age;
    private String addr;
    public Member() {
        System.out.println(&quot;Member 기본생성자&quot;);
    }
    public Member(String id) {
        System.out.println(&quot;Member(String id) 생성자&quot;);
        this.id = id;
    }
    public Member(String id ,String pwd) {
        System.out.println(&quot;Member(String id ,String pwd) 생성자&quot;);
        this.id = id;
        this.pwd = pwd;
    }
    public Member(String id ,int age) {
        System.out.println(&quot;Member(String id ,int age) 생성자&quot;);
        this.id = id;
        this.age = age;
    }
    public Member(String id, int age, String addr) {
        System.out.println(&quot;Member(String id, int age, String addr) 생성자&quot;);

        this.id = id;
        this.age = age;
        this.addr = addr;
    }
    public Member(String id, String pwd, int age, String addr) {
        this.id = id;
        this.pwd = pwd;
        this.age = age;
        this.addr = addr;
        System.out.println(&quot;Member(String id, String pwd, int age, String addr) 생성자&quot;);

    }
}
</code></pre>
<h4 id="memberdaojava">MemberDao.java</h4>
<pre><code class="language-java">public class MemberDao {
    public MemberDao() {
        System.out.println(&quot;MemberDao 생성자입니다.&quot;);
    }

    public void insert(Member member) {
        System.out.println(&quot;MemberDao의 insert 호출됨....&quot;);
        System.out.println(&quot;member = &quot; + member);
    }
}</code></pre>
<h4 id="memberservice">MemberService</h4>
<pre><code class="language-java">public class MemberService {
    private MemberDao memberDao;
    private Member member;

    public  MemberService() {
        System.out.println(&quot;MemberService 생성자 호출&quot;);
    }

    public MemberService(MemberDao memberDao, Member member) {
        this.memberDao = memberDao;
        this.member = member;
        System.out.println(&quot;MemberService(MemberDao memberDao, Member member) 생성자 호출&quot;);
        System.out.println(&quot;memberDao =&quot; + memberDao);
        System.out.println(&quot;member =&quot; + member);
    }

    public void serviceInsert() {
        System.out.println(&quot;MemberService의 serviceInsert 호출됨...&quot;);
        memberDao.insert(member);
    }
}</code></pre>
<h4 id="springdixml">springDI.xml</h4>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    1. 기본생성자
    &lt;bean class=&quot;sample02.Member&quot; id=&quot;m1&quot;/&gt;

    2. 인수 1개 받는 생성자
    &lt;bean class=&quot;sample02.Member&quot; id=&quot;m2&quot;&gt;
        &lt;constructor-arg value=&quot;jang&quot;/&gt;
    &lt;/bean&gt;

    3. 인수 2개 받는 생성자
    &lt;bean class=&quot;sample02.Member&quot; id = &quot;m3&quot;&gt;
        &lt;constructor-arg value=&quot;kim&quot;/&gt;
        &lt;constructor-arg value=&quot;20&quot; type=&quot;int&quot;/&gt;
    &lt;/bean&gt;
          인수가 2개 받는 생성자가 2개! -&gt; 갯수가 2개인 애들 먼저 찾음
          type 명시 안하면 코드상 가장 먼저있는 인수 2개 받는 생성자 호출

    4. 인수 4개 받는 생성자
    &lt;bean class=&quot;sample02.Member&quot; id =&quot;m4&quot;&gt;
        &lt;constructor-arg value=&quot;king&quot; index=&quot;0&quot; /&gt;
        &lt;constructor-arg value=&quot;30&quot; index=&quot;2&quot;/&gt;
        &lt;constructor-arg value=&quot;서울&quot; index=&quot;3&quot;/&gt;
        &lt;constructor-arg value=&quot;1234&quot; index=&quot;1&quot;/&gt;
    &lt;/bean&gt;
          순서 안맞추면 에러 떨어짐 index 써줘서 맞춰주기!

      5. MemberDao 생성자
    &lt;bean class=&quot;sample02.MemberDao&quot; id=&quot;dao&quot;/&gt;

      6. MemberService 인수 2개 받는 생성자
    &lt;bean class=&quot;sample02.MemberService&quot; id=&quot;service&quot;&gt;
        &lt;constructor-arg ref=&quot;dao&quot;/&gt;
        &lt;constructor-arg ref=&quot;m4&quot;/&gt;
    &lt;/bean&gt;
          기본생성자로 생성하면 Member와 MemberDAO가 초기화되지 않음
        생성자를 이용한 주입(DI) - m4 넣어줌
              1) value : 일반타입 (프리미트 타입)
               2) ref : 객체

&lt;/beans&gt;</code></pre>
<p>기존 방식</p>
<pre><code class="language-java">Member member = new Member(&quot;jang&quot;, &quot;1234&quot;, 20, &quot;서울&quot;);
MemberDao memberDao = new MemberDao();
MemberService service = new MemberService(memberDao, member);
service.serviceInsert();</code></pre>
<pre><code class="language-java">public class MainTest {
    public static void main(String[] args) {

        ApplicationContext context
                = new ClassPathXmlApplicationContext(&quot;springDI.xml&quot;);

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

        MemberService service = context.getBean(&quot;service&quot;, MemberService.class);
        service.serviceInsert();
    }
}

1. Member 기본생성자
2. Member(String id) 생성자
3. Member(String id ,int age) 생성자
4. Member(String id, String pwd, int age, String addr) 생성자
5. MemberDao 생성자입니다.
6. MemberService(MemberDao memberDao, Member member) 생성자 호출
    memberDao = MemberDao()
    member = Member(id=king, pwd=1234, age=30, addr=서울)

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

MemberService의 serviceInsert 호출됨...
MemberDao의 insert 호출됨...
member = Member(id=king, pwd=1234, age=30, addr=서울)
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🏘️ 카프카 클러스터 구축]]></title>
            <link>https://velog.io/@dumbveloper_100/kafkacluster</link>
            <guid>https://velog.io/@dumbveloper_100/kafkacluster</guid>
            <pubDate>Fri, 22 Mar 2024 02:20:37 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/90d82ef8-493c-45da-8ecc-da9105d59bbb/image.png" alt=""></p>
<h3 id="1-vi-docker-composeyml">1. vi docker-compose.yml</h3>
<p>로컬에 다운받은 카프카 폴더에서 yml 파일 편집</p>
<pre><code class="language-yml">version: &#39;2.1&#39;

services:
  zoo1:
    image: confluentinc/cp-zookeeper:latest
    hostname: zoo1
    container_name: zoo1
    ports:
      - &quot;2182:2181&quot;
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_SERVER_ID: 1
      ZOOKEEPER_SERVERS: zoo1:2888:3888

  kafka1:
    image: confluentinc/cp-kafka:latest
    hostname: kafka1
    container_name: kafka1
    ports:
      - &quot;9093:9093&quot;
      - &quot;29093:29093&quot;
    environment:
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka1:19093,EXTERNAL://localhost:9093,DOCKER://host.docker.internal:29093
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_ZOOKEEPER_CONNECT: &quot;zoo1:2181&quot;
      KAFKA_BROKER_ID: 1
      KAFKA_LOG4J_LOGGERS: &quot;kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO&quot;
      KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: &quot;true&quot;
    depends_on:
      - zoo1

  kafka2:
    image: confluentinc/cp-kafka:latest
    hostname: kafka2
    container_name: kafka2
    ports:
      - &quot;9094:9094&quot;
      - &quot;29094:29094&quot;
    environment:
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka2:19094,EXTERNAL://localhost:9094,DOCKER://host.docker.internal:29094
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_ZOOKEEPER_CONNECT: &quot;zoo1:2181&quot;
      KAFKA_BROKER_ID: 2
      KAFKA_LOG4J_LOGGERS: &quot;kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO&quot;
      KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: &quot;true&quot;
    depends_on:
      - zoo1

  kafka3:
    image: confluentinc/cp-kafka:latest
    hostname: kafka3
    container_name: kafka3
    ports:
      - &quot;9095:9095&quot;
      - &quot;29095:29095&quot;
    environment:
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka3:19095,EXTERNAL://localhost:9095,DOCKER://host.docker.internal:29095
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_ZOOKEEPER_CONNECT: &quot;zoo1:2181&quot;
      KAFKA_BROKER_ID: 3
      KAFKA_LOG4J_LOGGERS: &quot;kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO&quot;
      KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: &quot;true&quot;
    depends_on:
      - zoo1</code></pre>
<p>로컬 2182 포트로 주키퍼 접속 (내부적으론 2181)
카프카 1 : 9093 포트
카프카 2 : 9094 포트
카프카 3 : 9095 포트</p>
<pre><code class="language-yml">    ports:
      - &quot;9095:9095&quot;
      - &quot;29095:29095&quot;</code></pre>
<blockquote>
<p><strong>9095:9095</strong>: 이 부분은 호스트의 9095 포트를 컨테이너의 9095 포트로 매핑
-&gt; 호스트에서 Kafka 브로커에 접속할 때 9095 포트를 사용한다는 의미
<strong>29095:29095</strong>: Docker 내부 네트워크에서의 포트 매핑을 정의 
-&gt; 컨테이너 내부에서 Kafka 브로커에 접속할 때 사용할 포트 지정.
컨테이너 내부에서 29095 포트를 사용하여 Kafka 브로커에 접속할 수 있음.</p>
</blockquote>
<h3 id="1-실행">1. 실행</h3>
<pre><code class="language-bash">$docker-compose -f docker-compose.yml up -d # yml에 포함된 컨테이너들 올리는 명령어
$docker ps # docker ps 명령어로 잘 돌아가나 확인</code></pre>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/7f5f051d-d6bf-4ed0-9316-cf738003b436/image.png" alt=""></p>
<h3 id="2-토픽생성">2. 토픽생성</h3>
<pre><code class="language-bash"># 로컬 카프카 폴더내에서
./bin/kafka-topics.sh --create \
    --replication-factor 3 \
    --partitions 3 \
    --topic app-push-topic \
    --bootstrap-server localhost:9093,localhost:9094,localhost:9095</code></pre>
<ul>
<li>&quot;app-push-topic&quot;이라는 토픽을 생성하며, 각 파티션에는 3개의 복제본이 있고, 파티션 수는 3개</li>
<li>실행하면 Kafka 클러스터 내에서 해당 토픽이 생성되고, 관련된 브로커 간에 파티션 복제가 설정됨<pre><code class="language-bash"># 토픽 상세 조회
./bin/kafka-topics.sh --describe \
  --topic app-push-topic \
  --bootstrap-server localhost:9093,localhost:9094,localhost:9095
</code></pre>
</li>
</ul>
<p>#결과
Topic: app-push-topic    TopicId: C__MUpdpTv-Ej1s8Empw8A    PartitionCount: 3    ReplicationFactor: 3    Configs:
    Topic: app-push-topic    Partition: 0    Leader: 1    Replicas: 1,2,3    Isr: 2,3,1
    Topic: app-push-topic    Partition: 1    Leader: 2    Replicas: 2,3,1    Isr: 2,3,1
    Topic: app-push-topic    Partition: 2    Leader: 3    Replicas: 3,1,2    Isr: 3,2,1</p>
<pre><code>![](https://velog.velcdn.com/images/dumbveloper_100/post/a9b34394-7f83-4641-8788-f33c63510653/image.png)
- **Partition: 0, 1, 2**: 각 파티션의 번호
- **Leader: 1, 2, 3**: 각 파티션의 리더 브로커의 ID
- **Replicas: 1,2,3, 2,3,1, 3,1,2**: 각 파티션에 대한 복제본(Replica)의 브로커 ID 목록. 순서는 중요하지 않음!
- **Isr: 2,3,1, 2,3,1, 3,2,1**: 현재 인-시크(In-Sync Replica) 목록. 각 파티션의 리더 브로커를 포함한 현재 복제본의 목록

&gt; 📌 ***인-시크란?***
Kafka에서 각 파티션은 여러 개의 복제본을 가질 수 있음,  이 중에서 하나는 리더이며, 나머지는 팔로워 복제본. 리더 파티션은 메시지를 쓰고 소비하는 역할을 수행하며, 팔로워 복제본들은 리더와 동기화되어 있어 리더에 장애가 발생했을 때 대체 가능.
→ 인-시크는 현재 메시지를 따라가는 동기화된 상태에 있는 복제본들을 나타냄. 즉, 인-시크에 속한 복제본들은 리더와 동일한 메시지를 가지고 있으며, 데이터의 일관성을 보장하는것 
→ 만약 리더 파티션에서 메시지를 송신하고 있는데 팔로워 중 하나가 동기화되지 못한 상태라면 해당 복제본은 인-시크에서 제외됨.

### 쉘 4개 이제 열어서 , 1개 프로듀서 3개 컨슈머 실행
```bash
# consumer jar 파일 있는 디렉토리에서
java -jar consumer-2.3.0.RELEASE.jar

# producer jar 파일 있는 디렉토리에서
java -jar producer-2.3.0.RELEASE.jar</code></pre><p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/8435555b-dbf7-4d81-9d20-e2f82284e0ce/image.png" alt=""></p>
<h3 id="postman으로-test">postman으로 TEST</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/2fa2a347-1a01-4e42-93da-c812f56cadee/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/dd552530-760f-47ab-96dc-e5f072f30006/image.png" alt="">
분산처리 확인완료</p>
<hr>
<pre><code class="language-bash"># 컨슈머 그룹 리스트 조회
./bin/kafka-consumer-groups.sh --list \
    --bootstrap-server localhost:9093,localhost:9094,localhost:9095

app-push-group

# 컨슈머 그룹 상세조회
./bin/kafka-consumer-groups.sh --describe \
    --group app-push-group \
    --bootstrap-server localhost:9093,localhost:9094,localhost:9095

GROUP           TOPIC           PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             CONSUMER-ID                                                    HOST            CLIENT-ID
app-push-group  app-push-topic  0          264             264             0               consumer-app-push-group-1-a9d6852b-1f81-400b-8337-6466a1f0aab2 /192.168.65.1   consumer-app-push-group-1
app-push-group  app-push-topic  2          248             248             0               consumer-app-push-group-1-dc22572f-eddf-4ed2-8299-d6da8db4edfa /192.168.65.1   consumer-app-push-group-1
app-push-group  app-push-topic  1          270             270             0               consumer-app-push-group-1-c17f0f35-d5a2-48d4-8952-9c9f9cc820fd /192.168.65.1   consumer-app-push-group-1</code></pre>
<ul>
<li>GROUP | Topic |  PARTITION : 조회한 컨슈머 그룹이 마지막으로 커밋한 토픽과 파티션을 나타냄</li>
<li>CURRENT-OFFSET : 컨슈머 그룹이 가져간 토픽의 파티션에 가장 최신 오프셋(offset)이 몇번 인지 나타냄<ul>
<li>오프셋 : 파티션의 각 레코드에 할당된 번호 , 데이터가 파티션에 들어올때마다 1씩 증가</li>
</ul>
</li>
<li>LOG-END-OFFSET : 해당 컨슈머 그룹의 컨슈머가 어느 오프셋까지 커밋했는지 알 수 있다<ul>
<li>CURRENT-OFFSET ≤ LOG-END-OFFSET</li>
</ul>
</li>
<li>LAG : 컨슈머 그룹이 토픽의 파티션에 있는 데이터를 가져가는데 얼마나 지연이 발생하나 나타내는 지표<ul>
<li>LOG-END-OFFSET(컨슈머 그룹이 커밋한 오프셋) - CURRENT-OFFSET(해당 파티션의 가장 최신 오프셋) = LAG</li>
</ul>
</li>
<li>CONSUMER-ID : 컨슈머의 할당을 내부적으로 구분하기 위한 id</li>
<li>HOST : 컨슈머가 동작하는 host 명 , 이 값을 통해 컨슈머의 호스트명 또는 ip를 알아 낼 수 있다.</li>
<li>CLIENT-ID : 컨슈머에 할당된 id , 사용자가 지정가능 , 지정하지 않으면 자동생성됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ Spring Cloud 환경 구축하기(Eureka)]]></title>
            <link>https://velog.io/@dumbveloper_100/Spring-Cloud-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0Eureka</link>
            <guid>https://velog.io/@dumbveloper_100/Spring-Cloud-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0Eureka</guid>
            <pubDate>Wed, 06 Mar 2024 02:15:53 GMT</pubDate>
            <description><![CDATA[<h3 id="🧱시스템-구조">🧱시스템 구조</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/49d6a3d2-b31b-4f5c-a7c6-6537567cb106/image.png" alt=""></p>
<h3 id="🍀-eureka-란">🍀 Eureka 란?</h3>
<p>Eureka는 클라우드 환경의 다수의 서비스(예: API 서버)들의 로드 밸런싱 및 장애 조치 목적을 가진 미들웨어서버!</p>
<blockquote>
<p><strong>로드 밸런싱</strong> : 특정 서비스를 제공하는 서버가 여러대가 있을 때 트래픽을 한 서버에 몰리지 않게 분산해주는 기술이다.
<strong>미들웨어</strong> : 데이터를 주고 받는 양쪽의 서비스(웹의 예로 클라이언트와 API 서버)의 중간에 위치해 매개 역할을 하는 소프트웨어다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/694f6f14-d023-4c58-8ed2-0f220a861673/image.png" alt="">
<strong>Eureka</strong>는 이러한 미들웨어 기능을 하기 위해 각 연결된 서비스의 IP / PORT /InstanceId를 가지고 있고 REST기반으로 작동.
<strong>Eureka</strong>는 Client-Sever 방식으로 Eureka Server에 등록된 서비스는 Eureka Client로 불린다.
각 서비스를 Eureka Server에 등록하게 되면 Eureka Server는 각 Eureka Client의 IP / PORT / InstanceId를 저장한다.
이후 Eureka Client가 다른 Eureka Client에게 요청을 보낼 때 Eureka에서 받아온 정보를 가지고 요청을 보낼 수 있다.</p>
<h3 id="1-eureka-server">1. Eureka server</h3>
<pre><code class="language-xml">       &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-server&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;!-- 모니터링 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;!--Spring Boot Admin Server를 시작하는 데 필요한 라이브러리--&gt;
        &lt;!-- 2024-03-04 시점엔 3.2.3 버전 없음 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;de.codecentric&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-admin-starter-server&lt;/artifactId&gt;
            &lt;version&gt;3.2.2&lt;/version&gt;
        &lt;/dependency&gt;</code></pre>
<pre><code class="language-java">@EnableAdminServer
@EnableEurekaServer
@SpringBootApplication
public class Step07SpringCloudServerApplication {

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

}</code></pre>
<ul>
<li><strong>@EnableAdminServer</strong> :  어노테이션은 Spring Boot Admin Server를 활성화하는 데 사용, 모니터링 및 관리를 제공하는 도구.</li>
<li><strong>@EnableEurekaServer</strong> :  현재의 스프링 부트 애플리케이션을 Eureka Server로 설정</li>
</ul>
<p>application.yml</p>
<pre><code class="language-yml">server:
  port: 8761
spring:
  application:
    name: server
  boot:  # spring boot admin 모니터링 기능 이용을 위한 설정 부분
    admin:
      context-path: /admin
management: # 엔트포인트 노출 설정 애플리케이션을 배포하는 경우 인증 없이 모든 액추에이터 끝점에 액세스
  endpoints:
    web:
      exposure:
        include: &quot;*&quot;
eureka:
  instance:
    hostname: &lt;ec2 퍼블릭IP&gt;
    prefer-ip-address: true
  client:
    register-with-eureka: false
    service-url:
      default-zone: http://&lt;퍼블릭IP&gt;:8761/eureka/</code></pre>
<h3 id="2-eureka-client-server">2. Eureka client server</h3>
<pre><code class="language-xml">
        &lt;dependency&gt;  
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;de.codecentric&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-admin-starter-client&lt;/artifactId&gt;
            &lt;version&gt;3.2.2&lt;/version&gt;
        &lt;/dependency&gt;

</code></pre>
<pre><code class="language-java">@EnableDiscoveryClient
@SpringBootApplication
public class Step07SpringCloudClient1ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(Step07SpringCloudClient1ConsumerApplication.class, args);
    }
}</code></pre>
<ul>
<li><strong>@EnableDiscoveryClient</strong> : 서비스 디스커버리 시스템에 등록되어 다른 마이크로서비스들과 통신</li>
</ul>
<pre><code class="language-yml">server:
  port: 9009
  address: &lt;ec2 private ip&gt;

# project 별칭 등록
# 다른 project에서 호출할때 사용되는 이름
spring:
  application:
    name: PRODUCER-SERVICE
  boot:
    admin:
      client:
        instance:
          service-url: http://&lt;public ip&gt;:9009
# 애플리케이션의 상태, 메모리 사용량, 데이터베이스 연결 상태 등을 모니터링하고 관리하기 위한 설정         
# Spring Boot Actuator를 사용하여 애플리케이션의 상태를 모니터링하고 관리 가능
# 필요한 경우 설정을 조정하여 원하는 엔드포인트를 노출하여 관리 가능        
management: 
  endpoints:
    web:
      exposure:
        include: &quot;*&quot;

eureka:
  instance:
    prefer-ip-address: true
    ip-address: ${server.address}
    instance-id: ${server.address}:${spring.application.name}:${server.port} # spring Eureka에 등록되는 status 값
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://&lt;유레카 public ip&gt;:8761/eureka/</code></pre>
<h3 id="ec2-4개에-각-server--consumer1--consumer2--producer1-올리고-모니터링">ec2 4개에 각 Server , Consumer1 , Consumer2 , Producer1 올리고 모니터링</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/799d0681-929a-4ec4-b37d-2b39904ebfa9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/760d570a-48bf-44fb-ac92-147d44959551/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/ae1c5afe-2d39-43da-9de4-6c63ba8d2306/image.png" alt=""></p>
<h3 id="서로-다른-서버의-메소드-호출">서로 다른 서버의 메소드 호출!</h3>
<ul>
<li><p>User</p>
<pre><code class="language-java">@Component
public class DifferentClientLogicConsumer {

  @Autowired
  private DiscoveryClient client;

  public String getClient1ServiceLogic() {
      //해당 이름으로 등록된 spring cloud 앱들 반환
      List&lt;ServiceInstance&gt; siList = client.getInstances(&quot;PRODUCER-SERVICE&quot;);

      //PRODUCER-SERVICE 로 등록된 기능의 spring cloud client 앱객체
      ServiceInstance si = siList.get(0);

      //PRODUCER-SERVICE
      System.out.println(&quot;User  요청 처리 &quot; + si);

      String url = si.getUri() + &quot;/secondclient&quot;;
      System.out.println(url);
      RestTemplate rt = new RestTemplate();

      String response = rt.getForObject(url, String.class);
      return response;
  }
}</code></pre>
<pre><code class="language-java">@Slf4j
@RestController
public class ThirdClientController {

  @Autowired
  private DifferentClientLogicConsumer consumer; 

  //http://localhost:&lt;&gt;/secondclient
  @GetMapping(&quot;/user&quot;)
  public String first() {
      System.out.println(&quot;Consumer2의 first() (Get/user) 메소드 ***&quot;);

      return &quot;Consumer 응답 데이터 &quot; + consumer.getClient1ServiceLogic();
  }
</code></pre>
</li>
</ul>
<p>}</p>
<pre><code>- producer
```java
    //http://localhost:9009/secondclient
    @GetMapping(&quot;/secondclient&quot;)
    public String second() {
        System.out.println(&quot;Producer의 first() 메소드 호출 ***&quot;);
        return &quot;알레스카 사는 김상덕씨&quot;;
    }</code></pre><h3 id="producer-port-9009---user-port-8090">producer port (9009) ,  User port (8090)</h3>
<p>하지만 user에서 producer의 메소드를 부르고 있음!
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/b7d20050-1917-4b9b-8609-90fd6b7721cc/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_5397_키로거]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%805397%ED%82%A4%EB%A1%9C%EA%B1%B0</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%805397%ED%82%A4%EB%A1%9C%EA%B1%B0</guid>
            <pubDate>Tue, 05 Mar 2024 14:21:55 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/5397">https://www.acmicpc.net/problem/5397</a></p>
<p>두 개의 Stack(key, delete)을 사용하여 풀이하였다. 이유는 두가지이다.</p>
<p>&#39;-&#39; :  keyStack에 가장 최근에 들어간 문자열을 삭제해줘야함. (LIFO)
&#39;&lt;&#39; or &#39;&gt;&#39; : 커서 위치 변경은 삽입 순서가 바뀌는 것이다.
&#39;&lt;&#39;을 할 때 left에 가장 최근에 들어간 문자열을 right에 저장해준다.
&#39;&gt;&#39;을 할 때는 right 있는 문자열을 꺼내준다음 left에 넣어준다.
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/fdecba08-98a8-4db3-8a30-81fecff80a38/image.png" alt=""></p>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;

public class Main {
    static int T;

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

        for (int t = 0; t &lt; T; t++) {
            String str = br.readLine();
            Stack&lt;Character&gt; left = new Stack&lt;&gt;();
            Stack&lt;Character&gt; right = new Stack&lt;&gt;();

            for (int i = 0; i &lt; str.length(); i++) {
                char c = str.charAt(i);

                switch (c) {
                    case &#39;&lt;&#39;:
                        if (!left.isEmpty()) {
                            right.push(left.pop());
                        }
                        break;
                    case &#39;&gt;&#39;:
                        if (!right.isEmpty()) {
                            left.push(right.pop());
                        }
                        break;
                    case &#39;-&#39;:
                        if (!left.isEmpty()) {
                            left.pop();
                        }
                        break;
                    default:
                        left.push(c);
                        break;
                }
            }

            StringBuilder sb = new StringBuilder();
            while (!left.isEmpty()) {
                right.push(left.pop());
            }
            while (!right.isEmpty()) {
                sb.append(right.pop());
            }

            System.out.println(sb);
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_3273_두수의합]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%803273%EB%91%90%EC%88%98%EC%9D%98%ED%95%A9</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%803273%EB%91%90%EC%88%98%EC%9D%98%ED%95%A9</guid>
            <pubDate>Mon, 04 Mar 2024 14:33:14 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/3273">https://www.acmicpc.net/problem/3273</a>
주어진 배열에서 합이 X가 되는 쌍의 개수를 구하는 문제</p>
<h3 id="🥲-dfs-조합으로-풀이시-시간초과-발생-">🥲 dfs 조합으로 풀이시 시간초과 발생 !</h3>
<pre><code class="language-java">public class Song3273 {
    static int N;
    static int[] arr;
    static int X;
    static int ans;

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

        N = Integer.parseInt(br.readLine());
        arr = new int[N];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }
        X = Integer.parseInt(br.readLine());

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

    private static void findCombinations(int idx, int selectedCount, int currentSum) {
        // 종료 조건
        if (selectedCount == 2) {
            if (currentSum == X) {
                ans++;
            }
            return;
        }

        // 조합 생성
        for (int i = idx; i &lt; N; i++) {
            findCombinations(i + 1, selectedCount + 1, currentSum + arr[i]);
        }
    }
}
</code></pre>
<p>주어진 코드는 모든 가능한 조합을 DFS를 통해 생성하고, 합이 X인 경우를 찾아내는 방식으로 문제를 해결하려고 합니다. 그러나 이러한 방식은 입력의 크기가 커질수록 시간 복잡도가 급격하게 증가하게 되어 시간 초과가 발생할 수 있습니다.</p>
<p>빅 오 표기법에서 이 코드의 시간 복잡도를 나타내면 O(2^N)이 됩니다. 여기서 N은 배열의 크기를 의미.</p>
<p>백준 3273번 문제에서 주어진 입력 범위에 대해 고려해보면, N이 최대 10,000까지 가능합니다. 따라서 O(2^N)의 시간 복잡도는 매우 큰 값이 되어 효율적으로 해결하기 어려워집니다.</p>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Song3273 {
    static int N;
    static int[] arr;
    static int X;
    static int ans;

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

        N = Integer.parseInt(br.readLine());
        arr = new int[N];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }
        X = Integer.parseInt(br.readLine());

        Arrays.sort(arr); // 정렬!

        int left = 0, right = N - 1;

        while (left &lt; right) {
            int sum = arr[left] + arr[right];

            if (sum == X) {
                ans++;
                left++;
                right--;
            } else if (sum &lt; X) {
                left++;
            } else {
                right--;
            }
        }


        System.out.println(ans);
    }



}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub으로 프로젝트 관리- Issue, Project, Milestone]]></title>
            <link>https://velog.io/@dumbveloper_100/GitHub%EC%9C%BC%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-Issue-Project-Milestone</link>
            <guid>https://velog.io/@dumbveloper_100/GitHub%EC%9C%BC%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-Issue-Project-Milestone</guid>
            <pubDate>Fri, 01 Mar 2024 15:26:44 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-issue">📌 issue</h2>
<p>📑 새로운 추가될 가능, 개선 해야할 가능, 버그 등등 모든것이 이슈! 
팀 프로젝트에서는 <strong>모든 활동 내역에 대해서 이슈를 등록</strong>하고 그 이슈기반으로 작업을 진행하게 된다.
이슈를 등록할 때 자주 사용하는 템플릿을 등록해서 사용하는 방법이 효율적</p>
<h4 id="1-레포지토리의-settings로-들어간다">1. 레포지토리의 Settings로 들어간다.</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/8e2000cf-e01d-4ece-a639-6b11815756f0/image.png" alt=""></p>
<h4 id="2-general-탭에서-아래로-내려보면-features--issues-에서-set-up-templates-버튼을-클릭">2. General 탭에서 아래로 내려보면 Features &gt; Issues 에서 Set up templates 버튼을 클릭</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/a3cdbcc1-ea62-4273-a288-a04533fa4319/image.png" alt=""></p>
<h4 id="3-이슈-템플릿-생성">3. 이슈 템플릿 생성</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/d20df888-4353-44f3-9b79-3460436d13f6/image.png" alt="">
컨텐츠 내용을 작성하고 default title 을 줄 수 있음!
이슈 생성시 이름 앞에 [FEAT] 붙이는 설정
ex) [FEAT] 로그인 기능 구현</p>
<h4 id="4-템플릿으로-이슈-작성">4. 템플릿으로 이슈 작성</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/3d1c1b17-392d-4232-aa16-dffe510a77b2/image.png" alt="">
Projects 와 Milestone 은 미리 등록해놓아야 설정 가능함
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/f2a426e7-42f4-42c1-90b6-1a350552e812/image.png" alt="">
이슈를 만들면 이슈 번호가 주어짐!</p>
<h2 id="🏂-project칸반보드-생성">🏂 Project(칸반보드) 생성</h2>
<p>Organizations 이나 개인의 프로젝트를 나타냄
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/7c64c628-74e6-4add-b3ba-1ce1d22fae8d/image.png" alt=""></p>
<p><a href="https://github.com/woowacourse-teams/2023-shook/projects?query=is%3Aopen">2023-shook</a> 프로젝트의 칸반보드를 make copy 해서 개인 칸반보드를 생성
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/05903d0c-32b5-4a73-b062-f168cd932a8d/image.png" alt="">
아까 만든 이슈에서 칸반보드 설정을 넣어주어 표시되도록 함</p>
<h2 id="🪨-milestone-생성">🪨 Milestone 생성</h2>
<p>마일드스톤은 프로젝트 이슈 탭에서 생성가능
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/1a8b9b12-1cb0-4ab4-96ee-0720d018bbc7/image.png" alt=""></p>
<p>마일스톤으로 등록하면 관련 이슈와 진행도를 한 눈에 확인이 가능!
보통 큰 작업 개념이나 version 을 등록
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/da20d305-9b94-4cc8-926a-a4a9411b59cf/image.png" alt="">
아까 생성한 이슈에서 마일드스톤 설정을 넣어주어 표시되도록 함</p>
<hr>
<h4 id="✚-이슈-하나더-생성">✚ 이슈 하나더 생성</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/91114ac2-d899-4c21-b864-ee12054833c0/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/32208253-d48c-40ef-920d-b536ad06c161/image.png" alt="">
생성 후 이슈 Status 설정가능</p>
<h4 id="👨🔬-하나의-마일드스톤큰-작업에-두개의-이슈-생성-완료">👨‍🔬 하나의 마일드스톤(큰 작업)에 두개의 이슈 생성 완료</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/6e00e4e3-1bb0-46cc-9bad-37c210eb9ddf/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/ed1e80ef-3b08-4f46-a59a-10f2277cc0bf/image.png" alt=""></p>
<p><strong>👍 일반적으로 개발 프로세스에서 각각의 이슈에 대해 별도의 브랜치를 생성하여 작업하는 것이 일반적임!</strong>
작업 흐름:</p>
<ol>
<li>이슈를 선택하고 해당 이슈 번호로 새로운 브랜치를 생성.
ex) feat/{issue-number}-{feature-name}
feature/#123-implement-new-feature</li>
<li>해당 브랜치에서 작업을 수행하고 변경사항을 커밋.</li>
<li>작업이 완료되면 원격 저장소로 push하고, 이슈에 대한 작업이 끝났음을 나타내기 위해 pull request를 생성.</li>
<li>pull request는 코드 리뷰와 테스트를 거쳐 메인 브랜치에 병합.</li>
</ol>
<h2 id="🕊️-intellij-task로-issue-브렌치-생성하기">🕊️ IntelliJ Task로 Issue 브렌치 생성하기</h2>
<p>😁 인텔리제이에서는 레포지토리에 존재하는 이슈로 브랜치를 생성하는 기능을 제공하고 있음.</p>
<h4 id="1-intellij---tasks-연동">1. IntelliJ - Tasks 연동</h4>
<p>인텔리제이의 Preferences 를 들어간 후 Tools &gt; Tasks &gt; Server에서 + 버튼을 누르고 Github 와 연동 , 정보를 입력하고 토큰을 발행해야함
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/10400bd1-88d1-4d6c-bfca-fbe380fb83bf/image.png" alt=""></p>
<h4 id="2-브렌치-생성">2. 브렌치 생성</h4>
<p>원래는 Dev 브렌치에서 파야하지만 Main을 Dev라고 가정하고 진행
인텔리제이 우상단 Default task 영역을 클릭 후, Open Task...를 클릭하면 아까 만들어 놓은 이슈를 확인가능!
(프로젝트이름)-(이슈번호):(이슈이름)
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/355c7b54-3576-45c2-a524-18d13a08af64/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/5c2d6d3f-cfff-45bd-b14b-7bb5eb9ffc77/image.png" alt="">
브랜치명(Create branch)을 명명 규칙에 맞게 수정하고 OK버튼을 클릭
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/2ee3b41e-ffb9-44c7-993b-140ea742fae5/image.png" alt="">
Default Task는 위에서 Create changelist에 등록한 이름으로 바뀌었고 terminal 에서 브렌치 생성 확인 가능</p>
<h2 id="🫸-인텔리제이에서-커밋-푸쉬">🫸 인텔리제이에서 커밋, 푸쉬</h2>
<p>feat/#1-이슈테스트1 브렌치에서 2개의 커밋을 만들고 push 해줌
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/537acba6-c0db-44d0-b4a9-7399a36213a9/image.png" alt=""></p>
<ol>
<li>오른쪽 하단에서 내 현재 <strong>브렌치 확인</strong>❗️</li>
<li>~ 작업 ~</li>
<li>왼쪽의 상단 세로의 Commit 버튼을 누르고 커밋. (Commit and Push 하여 Remote에도 바로 적용가능)
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/3b5b87ca-7f1e-4e77-a882-fc95a1336fce/image.png" alt="">
깃허브에서도 확인가능
사실 커밋 메세지도 아래와 같이 컨벤션을 맞춰줘야함 , 여기서는 그냥 테스트진행<pre><code>Feat: &quot;Add login API&quot;        // 타입: 제목
</code></pre></li>
</ol>
<p>로그인 API 개발               // 본문</p>
<p>Resolves: #123              // 꼬리말 =&gt; 이슈 123을 해결했으며,
Ref: #456                                 이슈 456 를 참고해야하며,
Related to: #48, #45         현재 커밋에서 아직 이슈 48 과 45 가 해결되지 않았다.</p>
<pre><code>
## 📤 Pull request 생성
PR 템블릿은 따로 생성 해줘야함
참고 : [Github - Pull request template 작성과 설정](https://green-bin.tistory.com/16)

Github에서 PR 탭 클릭하여 PR생성
_**🤬 브렌치가 어디서 어디로 합쳐지는지 꼭 잘 확인해야함!!**_
![](https://velog.velcdn.com/images/dumbveloper_100/post/dc7c8c94-b498-4b2f-a567-29e01ac274d4/image.png)

![](https://velog.velcdn.com/images/dumbveloper_100/post/1afa7b74-c340-49ff-a6e3-ae27c1e11ec6/image.png)
PR도 이슈처럼 내용을 작성해주고 Assigners, labels, projects, milestone 를 넣어줄 수 있음 + Reviewer
연관된 이슈 번호를 입력하여 추적 가능

#### PR을 받은 사람은 해당 브렌치의 커밋 코드 및 컨벤션을 점검하고 Merge함 , Merge 후에는 브렌치를 삭제 할 수 있음
![](https://velog.velcdn.com/images/dumbveloper_100/post/c0dcc3e9-c2e0-4754-9d9c-8c0df60a4e65/image.png)
프로젝트 칸반보드에서도 확인가능
![](https://velog.velcdn.com/images/dumbveloper_100/post/d78c2726-2d0d-468d-b594-c21f8b6c34b9/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 3 버전 실행 문제]]></title>
            <link>https://velog.io/@dumbveloper_100/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-3-%EB%B2%84%EC%A0%84-%EC%8B%A4%ED%96%89-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@dumbveloper_100/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-3-%EB%B2%84%EC%A0%84-%EC%8B%A4%ED%96%89-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Fri, 01 Mar 2024 12:55:34 GMT</pubDate>
            <description><![CDATA[<h2 id="🤔-30-부터는-java-17부터-지원">🤔 3.0 부터는 Java 17부터 지원!</h2>
<h2 id="1-project-settings에서-sdk를-17-버전으로-맞춤">1. Project Settings에서 SDK를 17 버전으로 맞춤</h2>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/daf6ba98-dd10-47cf-b5a9-323a60fba088/image.png" alt=""></p>
<h2 id="2-intellij-settings-에서-gradle-jvm-버전을-맞춰줌">2. IntelliJ Settings 에서 Gradle JVM 버전을 맞춰줌</h2>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/e55a92d7-107c-4c73-ad9c-3747f8dcc82b/image.png" alt=""></p>
<p>참고 : <a href="https://jojoldu.tistory.com/698">https://jojoldu.tistory.com/698</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준_15686_치킨배달]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%8015686%EC%B9%98%ED%82%A8%EB%B0%B0%EB%8B%AC</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%B0%B1%EC%A4%8015686%EC%B9%98%ED%82%A8%EB%B0%B0%EB%8B%AC</guid>
            <pubDate>Fri, 01 Mar 2024 12:03:24 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/15686">https://www.acmicpc.net/problem/15686</a></p>
<h1 id="풀이">풀이</h1>
<p>입력에 주어진 치킨집을 M개로 줄일때 최소 도시의 치킨거리 (집-치킨집 거리의 최소값들의 합) 을 구하는 문제</p>
<ul>
<li><p>조합 combination (dfs)</p>
</li>
<li><p>최소거리의 합의 최소를 구하는 문제! 👉 어떤 치킨집이 없어지지 않고 선택되어 남아있는지에 따라 거리가 달라짐</p>
<ul>
<li>특정 집 - 치킨집의 최소 거리를 구해야함 (<strong>치킨거리</strong>)</li>
<li>치킨거리 합의 최소값을 구해야함 (<strong>도시의 치킨거리</strong>)</li>
</ul>
</li>
</ul>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int N, M;
    static int ans = Integer.MAX_VALUE;
    static int[][] map;
    static boolean[] selected;
    static class Chicken{
        int x, y;
        public Chicken(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    static class House{
        int x, y;
        public House(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    static ArrayList&lt;Chicken&gt; chickens = new ArrayList&lt;&gt;();
    static ArrayList&lt;House&gt; houses = new ArrayList&lt;&gt;();

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        map = new int[N][N];

        for(int i = 0; i &lt; N; i++){
            st = new StringTokenizer(br.readLine());
            for(int j = 0; j &lt; N; j++){
                int n = Integer.parseInt(st.nextToken());
                map[i][j] = n;
                if(n == 1){
                    houses.add(new House(i, j));
                }
                if(n == 2){
                    chickens.add(new Chicken(i, j));
                }
            }
        }

        selected = new boolean[chickens.size()];
        dfs(0,0);
        System.out.print(ans);
    }

    static void dfs(int idx, int cnt){
        if(cnt == M){
            ans = Math.min(ans,distance());
            return;
        }

        for(int i = idx; i &lt; chickens.size(); i++){

            if (selected[i]) continue;

            selected[i] = true;
            dfs(i + 1, cnt + 1);

            selected[i] = false;
        }
    }

    static int distance(){
        int sum = 0;
        for (House house : houses) {
            int min = Integer.MAX_VALUE;
            for (int i = 0; i &lt; chickens.size(); i++) {
                if (selected[i]) {
                    Chicken chicken = chickens.get(i);
                    int d = Math.abs(house.x - chicken.x) + Math.abs(house.y - chicken.y);
                    min = Math.min(min, d);
                }
            }
            sum += min;
        }
        return sum;
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS VPC 네트워크 구축]]></title>
            <link>https://velog.io/@dumbveloper_100/AWS-VPC-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@dumbveloper_100/AWS-VPC-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Thu, 29 Feb 2024 09:30:07 GMT</pubDate>
            <description><![CDATA[<h2 id="1-vpc-생성">1. VPC 생성</h2>
<p>자체 데이터 센터에서 운영하는 기존 네트워크와 유사한 가상의 네트워크를 의미. 
우리는 AWS VPC를 사용하여 논리적으로 격리된 가상 네트워크를 구성하고 그 네트워크 안에서 AWS 리소스를 사용할 수 있게 되는 것!
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/de135242-92b9-4f70-8189-536be9a30250/image.png" alt=""></p>
<blockquote>
<h4 id="ipv4-cidr-블록">Ipv4 CIDR 블록</h4>
<p>💡10.0.0.0/16 으로 생성
-&gt; 10.0.0.0 ~ 10.0.255.255 IP 주소 범위를 가지게 된다는 것을 의미
VPC의 네트워크 주소: 10.0.0.0
최소 호스트 주소: 10.0.0.1
최대 호스트 주소: 10.0.255.254
브로드캐스트 주소: 10.0.255.255
👉 VPC 내에서 여러 서브넷을 생성하거나 다양한 리소스에 IP 주소를 할당할 수 있으며, 10.0.0.1부터 10.0.255.254까지의 주소 범위에서 가능!</p>
</blockquote>
<blockquote>
<h4 id="테넌시">테넌시</h4>
<p>VPC 리소스의 전용 하드웨어에서의 실행 여부를 뜻
하드웨어가 공유 하드웨어를 사용할 것인지 혹은 전용 하드웨어를 사용할 것인지를 선택하는 항목
전용을 선택할 경우 VPC 내의 인스턴스는 항상 전용 하드웨어에서 전용 인스턴스로 실행되며 별도 비용이 추가되게 됨!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/eb55d804-48be-4a2c-9a3a-703e64a2d6cc/image.png" alt=""></p>
<h3 id="📍-가용영역">📍 가용영역</h3>
<p><strong>가용 영역(Availability Zone)</strong>이란 AWS 하나의 리전 내에 중복 전원, 네트워킹 및 연결이 있는 하나 이상의 개별 데이터 센터를 의미.
여러 AZ를 사용하면 높은 가용성과 내결함성 및 확장성을 갖출 수 있게 되며, 가용영역이 다르면 독립되었음을 보장할 수 있으므로 가용 영역별로 서브넷을 제공하면 여러 서브넷을 동시에 이용하지 못하는 가능성을 낮출 수 있다.</p>
<h2 id="2-서브넷-생성">2. 서브넷 생성</h2>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/86f2d5a1-8c7b-4915-bda7-2347fb1258b4/image.png" alt="">
서브넷 4개 (private 2개, public 2개) 생성</p>
<h4 id="2-1서브넷을-생성할-vpc선택">2-1.서브넷을 생성할 VPC선택</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/7ad57517-c693-4af8-b600-f4492d09e76e/image.png" alt=""></p>
<h4 id="2-2서브넷-설정-선택">2-2.서브넷 설정 선택</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/593b5f40-68d7-493f-9f19-b5e864c608e5/image.png" alt=""></p>
<blockquote>
<h4 id="ipv4-서브넷-cidr-블록">Ipv4 서브넷 CIDR 블록</h4>
<p>💡10.0.1.0/24 으로 생성
-&gt; 10.0.1.0 ~ 10.0.1.255 IP 주소 범위를 가지게 된다는 것을 의미</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/48417783-1017-4d95-94ee-65f0a605e795/image.png" alt=""></p>
<blockquote>
<p>public1,2 와 private1,2 를 10.0.1.0/24 ~ 10.0.1.4/24 로 생성
public1 , private1 는 ap-northeast-2a
public2 , private2 는 ap-northeast-2c</p>
</blockquote>
<h3 id="2-3-vpc의-리소스-맵에서-생성확인">2-3. VPC의 리소스 맵에서 생성확인</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/73b95685-be73-4a25-aaae-ee06fe54cc64/image.png" alt=""></p>
<ul>
<li>nternet gateway 추가 설정해야만 네트워크와 인터넷 사이의 통신이 가능</li>
<li>igt 생략시 VPC 내의 리소스는 서로 통신 불가</li>
</ul>
<h2 id="3-인터넷-게이트웨이-igt-생성">3. 인터넷 게이트웨이 (IGT) 생성</h2>
<p>VPC 에서 생성된 네트워크와 인터넷 사이의 통신 수행
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/850bcfb8-8020-4d7f-be13-38ad1321efc6/image.png" alt=""></p>
<h3 id="3-1-vpc-하위의-인터넷-게이트웨이-선택-후-게이트웨이-생성">3-1. VPC 하위의 인터넷 게이트웨이 선택 후 게이트웨이 생성</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/d8886d18-ae21-47ab-9592-0975e2667d15/image.png" alt=""></p>
<h3 id="3-2-vpc-와-연결">3-2. VPC 와 연결</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/2ef02d53-9320-4520-b403-c07ed38f08b7/image.png" alt="">
작업에서 VPC 연결을 설정해주고 Attached 상태가 되는것을 확인</p>
<h2 id="4-natnetwork-address-translation-설정">4. NAT(Network Address Translation) 설정</h2>
<p>public 1, 2 에 NAT 게이트 웨이 설정
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/c5d0db7e-7d18-4a00-9141-10b8131361cd/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/4240f59e-dcbf-4493-916c-e6903aec86f8/image.png" alt=""></p>
<h2 id="5-라우팅-테이블-설정">5. 라우팅 테이블 설정</h2>
<p>subnet 사이의 통신 경로 설정을 위한 요소, 특정 서버에 접속할 때 꼭 경유해야 할 정보 보유, 모든 서브넷에 라우팅 table 작성해야 함!
속한 테이블</p>
<h3 id="5-1-public-라우팅-테이블-생성">5-1. Public 라우팅 테이블 생성</h3>
<p>vpc 를 선택하여 생성하고 라우팅 편집
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/3613f94d-6d65-4051-9d0c-0d9d73b2cd58/image.png" alt=""></p>
<blockquote>
<h4 id="로컬-라우팅-local-route-1000016">로컬 라우팅 (Local Route) 10.0.0.0/16</h4>
</blockquote>
<ul>
<li>대상(Target): VPC의 CIDR 블록 (예: 10.0.0.0/16)</li>
<li>VPC 내부의 로컬 트래픽을 처리. </li>
<li>모든 VPC 내부 통신은 VPC의 CIDR 블록을 통해 이 라우트에 의해 처리되는 것을 의미<h4 id="인터넷-라우팅-internet-route-00000">인터넷 라우팅 (Internet Route) 0.0.0.0/0</h4>
</li>
<li>대상(Target): 인터넷 게이트웨이의 ID 또는 인터넷 게이트웨이 자체</li>
<li>인터넷으로 나가는 트래픽을 처리. </li>
<li>대상이 &quot;0.0.0.0/0&quot;이므로, 모든 목적지로 가는 트래픽은 이 라우트를 통해 인터넷 게이트웨이로 전달되는 것을 의미.</li>
</ul>
<h3 id="5-2-서브넷-연결-선택-→-서브넷-연결-편집-→-public-subnet-두개-선택--저장">5-2. 서브넷 연결 선택 → 서브넷 연결 편집 → public subnet 두개 선택  저장</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/cff8e038-af0b-43ce-8d6a-db67b72ebebc/image.png" alt=""></p>
<h3 id="5-3-private-라우팅-테이블-생성">5-3. Private 라우팅 테이블 생성</h3>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/ea87eec5-cdde-4bdb-82d9-75c59338c9e1/image.png" alt=""></p>
<blockquote>
<h4 id="nat-게이트웨이-라우팅-nat-gateway-route">NAT 게이트웨이 라우팅 (NAT Gateway Route):</h4>
</blockquote>
<ul>
<li>대상(Target): NAT 게이트웨이의 ID 또는 NAT 게이트웨이 자체</li>
<li>프라이빗 서브넷에 있는 인스턴스들이 외부로 나가기 위해 사용하는 라우팅 규칙. 대상이 &quot;0.0.0.0/0&quot;이므로, 모든 목적지로 가는 트래픽은 이 라우트를 통해 NAT 게이트웨이로 전달<h4 id="vpc-리소스맵-구조">VPC 리소스맵 구조</h4>
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/926240e1-ff9d-416e-8c4e-039ab0b14104/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/804c653c-c5ca-4048-907b-e3a42e7a86e2/image.png" alt=""></p>
<h2 id="6-보안-그룹-생성">6. 보안 그룹 생성</h2>
<ul>
<li>점프 서버용 보안 그룹 (퍼블릿 서브넷에 위치)
인바운드 규칙 SSH -&gt; 소스 : 0.0.0.0/0
SSH는 원격으로 서버에 접속하기 위한 프로토콜로, 보통 시스템 관리 작업 등을 수행하기 위해 사용</li>
<li>로드 밸런서용 보안 그룹
유형 : HTTP -&gt; 소스 : 0.0.0.0/0
유형 : HTTPS -&gt; 소스 : 0.0.0.0/0
HTTP는 웹 서버와 웹 클라이언트 간에 데이터를 전송</li>
</ul>
<p><strong>퍼블릭 서브넷</strong>: 점프 서버 (Bastion Host)가 위치하는 서브넷. 외부에서 SSH와 같은 프로토콜을 이용하여 이 서버에 접속할 수 있음.</p>
<p><strong>프라이빗 서브넷</strong>: 보안상의 이유로 직접 외부로 노출되지 않는 서브넷. 실제 웹 서버, 데이터베이스 서버 등이 위치할 수 있음.</p>
<h2 id="7-점프-서버용-ec2-생성">7. 점프 서버용 EC2 생성</h2>
<p>EC2 인스턴스 생성시 VPC, 서브넷, 보안 그룹 설정 가능
 <img src="https://velog.velcdn.com/images/dumbveloper_100/post/c3bb4a20-7896-4a4f-84f1-3f3af66549b2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[가상 프라이빗 클라우드(VPC)란?]]></title>
            <link>https://velog.io/@dumbveloper_100/%EC%A0%90%ED%94%84%EC%84%9C%EB%B2%84</link>
            <guid>https://velog.io/@dumbveloper_100/%EC%A0%90%ED%94%84%EC%84%9C%EB%B2%84</guid>
            <pubDate>Thu, 29 Feb 2024 01:15:14 GMT</pubDate>
            <description><![CDATA[<h2 id="🖥-가상-프라이빗-클라우드vpc">🖥 가상 프라이빗 클라우드(VPC)</h2>
<h3 id="퍼블릭-클라우드-vs-프라이빗-클라우드">퍼블릭 클라우드 vs 프라이빗 클라우드</h3>
<h4 id="🗂퍼블릭-클라우드">🗂퍼블릭 클라우드</h4>
<ul>
<li>퍼블릭 클라우드는 공유 클라우드 인프라.</li>
<li>클라우드 벤더의 여러 고객은 데이터는 공유되지 않지만, <em>동일한 인프라에 액세스</em>. ex) 레스토랑의 모든 사람이 동일한 주방에서 주문하지만, 다른 요리가 서빙되는 것과 같음. </li>
<li>퍼블릭 클라우드 서비스 공급자에는 AWS, Google Cloud Platform, Microsoft Azure ...<blockquote>
<p> <strong>&quot;다중 테넌트&quot; 란?</strong> 
클라우드 공급업체의 여러 고객이 동일한 컴퓨팅 리소스를 사용하고 있음을 의미.
클라우드 고객은 리소스를 공유하기는 하지만, 서로를 인식하지 못하며 데이터가 완전히 분리.</p>
</blockquote>
<h4 id="📂프라이빗-클라우드">📂프라이빗 클라우드</h4>
</li>
<li>단일 테넌트. </li>
<li>프라이빗 클라우드는 하나의 조직에 독점적으로 제공되는 클라우드 서비스. </li>
<li>가상 프라이빗 클라우드(VPC)는 퍼블릭 클라우드 내의 프라이빗 클라우드 다른 누구도 VPC 고객과 VPC를 공유하지 않음.
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/4f29b058-715d-4058-9f21-685c50e940ba/image.png" alt=""></li>
</ul>
<h3 id="vpc가-퍼블릭-클라우드-내에서-어떻게-격리">VPC가 퍼블릭 클라우드 내에서 어떻게 격리?</h3>
<ul>
<li><strong>서브넷</strong> : 네트워크 내의 모든 사람이 사용할 수 없도록 예약된 네트워크 내의 IP 주소 범위로, 기본적으로 개인 용도로 네트워크의 일부를 분할. VPC에서 서브넷은 공개적으로 볼 수 있는 일반적인 IP 주소와 달리 공개 인터넷을 통해 액세스할 수 없는 개인 IP 주소</li>
<li><strong>VLAN</strong> : LAN은 인터넷을 사용하지 않고 모두 서로 연결된 근거리 통신망 또는 컴퓨팅 장치 그룹. VLAN은 가상 LAN임. 서브넷과 마찬가지로 VLAN은 네트워크를 분할하는 방법</li>
<li><strong>VPN</strong> : 가상 사설망(VPN)은 암호화를 사용하여 공용 네트워크 위에 사설 네트워크를 만듬. VPN 트래픽은 라우터, 스위치 등 공개적으로 공유되는 인터넷 인프라를 통과하지만, 트래픽이 변환되어 누구에게도 표시되지 않음.</li>
</ul>
<p>VPC에는 VPC 고객만 액세스할 수 있는 전용 서브넷과 VLAN이 있다!
-&gt; 퍼블릭 클라우드 내의 다른 사람이 VPC 내의 컴퓨팅 리소스에 액세스하는 것을 방지! ex) 테이블에 &quot;예약석&quot; 표시
-&gt; VPC 고객은 VPN을 통해 VPC에 연결 즉, VPC 안팎으로 전달되는 데이터가 다른 퍼블릭 클라우드 사용자에게 표시되지 않는것.</p>
<h3 id="vpc-와-서브넷">VPC 와 서브넷</h3>
<h4 id="vpc-virtual-private-cloud">VPC (Virtual Private Cloud)</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/60054d00-66ed-4c72-9c98-aa355b4f999c/image.png" alt=""></p>
<ul>
<li>논리적으로 격리된 사용자 전용 가상 네트워크. </li>
<li>복수의 AZ (Availibity Zone, 가용영역 ≒ 데이터센터) 걸친 형태</li>
<li>💡 AWS의 리소스들이 위치할 네트워크 망.<blockquote>
<p>프로젝트에 사용되는 리소스 (EC2, RDS, S3 등등)이 물리적으로 하나의 데이터센터에만 위치해있다면 해당 센터에 문제가 생긴다면 서비스가 전체적으로 다운타임 발생!!!
👌 가상네트워크를 이용하여 물리적으로는 다른곳에 위치하지만 같은 사설망 IP 대역에 위치하게 만들어 리소스들끼리 통신할 수 있게 만들어주는 기술</p>
</blockquote>
</li>
</ul>
<h4 id="서브넷-subnet">서브넷 (subnet)</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/aafcaa44-4ad7-4242-a13a-b5163546d93f/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/abed0c8b-f6b0-4a7b-80cb-d935c6d7c063/image.png" alt=""></p>
<ul>
<li>VPC의 영역안에서 망을 더 쪼개는 행위</li>
<li>하지만 VPC와는 달리 지역적으로 나누기때문에 여러 AZ(가용영역)에 걸쳐있는것이 아닌 단일 AZ에 위치함.</li>
<li>대표적으로 보안과 네트워크의 브로드캐스트의 영역을 축소시켜서 통신 성능을 올리기 위함.</li>
</ul>
<h3 id="public-서브넷--private-서브넷-나누기">public 서브넷 , private 서브넷 나누기</h3>
<h4 id="public-subnet">Public Subnet</h4>
<ul>
<li>외부에서 접근이 가능한 네트워크영역</li>
<li>서브넷이 <strong><em>인터넷 게이트웨이로 향하는</em></strong> 라우팅이 있는 라우팅 테이블과 연결되는 경우 퍼블릭 서브넷이라 함.<ul>
<li>인터넷 게이트웨이(igw)는 VPC의 구성요소로써 VPC와 인터넷간에 통신을 할 수 있게 만들어주는 역할</li>
<li>서브넷이 바라보는 라우팅테이블에 인터넷게이트웨이가 등록되어있는 것</li>
</ul>
</li>
<li>해당 서브넷은 인터넷과 연결이 가능하므로 해당 서브넷에 위치한 리소스들은 공인IP를 가질 수 있음.</li>
</ul>
<h4 id="private-subnet">Private Subnet</h4>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/a3bb7f80-dfd8-4424-acf8-5b2004eb0fcc/image.png" alt=""></p>
<ul>
<li>외부에서 다이렉트로 접근이 불가능한 네트워크 영역</li>
<li>서브넷의 라우팅 테이블에 인터넷게이트웨이가 등록되어있지 않으므로 해당 서브넷에 위치한 리소스들은 외부와의 연결이 불가능</li>
</ul>
<h3 id="nat-network-address-translation">NAT (Network Address Translation)</h3>
<ul>
<li>프라이빗 서브넷에 DB가 있다고 가정하면, DB버전을 업그레이드하기위해서 인터넷 연결이 필요할 수 있음!</li>
<li><blockquote>
<p>이때 NAT 게이트웨이를 이용하여, 내부에서 외부로만 접근이 가능하게 만들어 줄 수 있는 것.</p>
</blockquote>
</li>
<li>외부에서 NAT 게이트웨이를 이용하여 접속을 불가능!!❌
여기서 말하는 외부란, VPC영역이 다른것을 의미. 
같은 VPC영역에 있으면 private IP로 접근이 가능!</li>
</ul>
<p>NAT 게이트웨이 설정 방법</p>
<ol>
<li>퍼블릭 서브넷에 NAT 게이트웨이에 탄력적IP(EIP)를 할당하여 생성</li>
<li>프라이빗 서브넷의 라우팅 테이블에 등록.</li>
</ol>
<p>즉, 프라이빗 서브넷에서 라우팅 테이블을 보고 인터넷으로 나가는 규칙 (0.0.0.0)에 대해서는 NAT 게이트웨이를 타고 EIP(공인 IP)로 변환 후 igw를 통해 인터넷으로 나가는것입니다.</p>
<p>이렇게 불편해지는데 프라이빗 서브넷을 사용하는 이유는, 엄격하게 다뤄야할 리소스들을 안전하게 관리하기 위함.</p>
<p>앞서 예를든것처럼, DB를 프라이빗 서브넷에 위치시킨다던가, ELB(로드벨런싱)만 퍼블릭영역에 두고 실제 WAS는 프라이빗영역에 배치하여 보다 안전하게 관리할 수 있다~!</p>
<p>참고
<a href="https://dontbesatisfied.tistory.com/13">https://dontbesatisfied.tistory.com/13</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SpringBoot App S3와 RDS 연동]]></title>
            <link>https://velog.io/@dumbveloper_100/SpringBoot-App-S3%EC%99%80-RDS-%EC%97%B0%EB%8F%99</link>
            <guid>https://velog.io/@dumbveloper_100/SpringBoot-App-S3%EC%99%80-RDS-%EC%97%B0%EB%8F%99</guid>
            <pubDate>Wed, 28 Feb 2024 08:12:23 GMT</pubDate>
            <description><![CDATA[<p>RDS는 mysql로 생성되어 있다고 가정</p>
<h2 id="1-버킷-생성">1. 버킷 생성</h2>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/b0d4c3e0-8c33-4b4f-8747-760155b3937b/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/049dfdb7-d7c1-4048-9e95-324c898af005/image.png" alt="">
접근을 위해 퍼블릭 엑세스를 허용 해주자!</p>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/5b5444e1-33f1-4d4e-9fae-6ea5365be652/image.png" alt=""></p>
<p>버킷 버전 관리를 활성화 할 경우 요금이 과금될 수 있음!
IAM을 통해 만들어진 user 의 <em>Access key ID,Secret access key</em> 가 필요!</p>
<ul>
<li>외부에 노출되지 않게 조심해야함!</li>
</ul>
<p>버킷이 퍼블릭 엑세스 허용되도록 생성된 것을 확인
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/edd9aa4b-1179-4926-b774-1f4f2c69beb0/image.png" alt=""></p>
<h2 id="2-spring-설정">2. Spring 설정</h2>
<h3 id="buildgradle-추가">build.gradle 추가</h3>
<pre><code>implementation &#39;org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE&#39;</code></pre><h3 id="yml-파일">yml 파일</h3>
<pre><code class="language-yml">server:
  port: 80    # server.port=80
  servlet:
    context-path: /probono  # server.servlet.context-path=/probono

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: &#39;계정&#39;
    password: &#39;비번&#39;
    url: jdbc:mysql://&lt;엔드포인트&gt;:3306/&lt;데이터베이스&gt;?useSSL=false&amp;allowPublicKeyRetrieval=true

  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL8Dialect
    generate-ddl: true
    hibernate:
      ddl-auto: none
    show-sql: true
  mvc:        # jsp 사용을 위한 필수 설정, WEB-INF 폴더 직접 만들고, pom.xml에 설정, ProbonoController의 메소드 setViewName()확인하기
    view:
      prefix: /WEB-INF/    # spring.mvc.view.prefix: /WEB-INF/
      suffix: .jsp         # spring.mvc.view.suffix: .jsp


  servlet:
    multipart:
      max-request-size: 30MB
      max-file-size: 30MB

cloud:
  aws:
    s3:
      bucket: &lt;버킷이름&gt;
    region:
      static: ap-northeast-2
    stack:
      auto: false
    credentials:
      access-key: &lt;키&gt;
      secret-key: &lt;키&gt;
</code></pre>
<p>중요한 정보가 담겨있음!! -&gt; yml 파일은 절~대 github에 올리지 않도록 .gitignore를 꼭 설정해 주자</p>
<h2 id="3-간단한-사진-업로드-기능작성">3. 간단한 사진 업로드 기능작성</h2>
<h3 id="config-파일">config 파일</h3>
<pre><code class="language-java">@Configuration
public class S3config {

    @Value(&quot;${cloud.aws.credentials.access-key}&quot;)
    private String accessKey;
    @Value(&quot;${cloud.aws.credentials.secret-key}&quot;)
    private String secretKey;
    @Value(&quot;${cloud.aws.region.static}&quot;)
    private String region;

    @Bean
    public AmazonS3Client amazonS3Client() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        return (AmazonS3Client)AmazonS3ClientBuilder
            .standard()
            .withRegion(region)
            .withCredentials(new AWSStaticCredentialsProvider(credentials))
            .build();
    }
}
</code></pre>
<h3 id="service">Service</h3>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class S3UploadService {

    private final AmazonS3 amazonS3;

    @Value(&quot;${cloud.aws.s3.bucket}&quot;)
    private String bucket;

    public String saveFile(MultipartFile multipartFile) throws IOException {
        String originalFilename = multipartFile.getOriginalFilename();

        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(multipartFile.getSize());
        metadata.setContentType(multipartFile.getContentType());

        amazonS3.putObject(bucket, originalFilename, multipartFile.getInputStream(), metadata);
        return amazonS3.getUrl(bucket, originalFilename).toString();
    }
}
</code></pre>
<h3 id="controller">Controller</h3>
<pre><code class="language-java">@RestController
@RequiredArgsConstructor
public class FileUploadController {

    private final S3UploadService s3UploadService;

    @PostMapping(&quot;/api/upload&quot;)
    public ResponseEntity&lt;String&gt; upload(@RequestPart(name = &quot;file&quot;) MultipartFile file) throws IOException {
        return ResponseEntity.ok(s3UploadService.saveFile(file));
    }

}</code></pre>
<h4 id="😢오류">😢오류!</h4>
<p>수정 전</p>
<pre><code class="language-java">@PostMapping(&quot;/api/upload&quot;)
    public ResponseEntity&lt;String&gt; upload(@RequestPart MultipartFile file) throws IOException {
        return ResponseEntity.ok(s3UploadService.saveFile(file));
    }


오류 발생
Request part name for argument type [org.springframework.web.multipart.MultipartFile] not specified, and parameter name information not found in class file either.</code></pre>
<p>파라미터 이름 정보가 클래스 파일에서 찾을 수 없다는 에러가 발생한다!</p>
<p>메서드의 파라미터에 이름을 명시적으로 지정하여 해결해줬다.</p>
<p>수정 후</p>
<pre><code class="language-java">@PostMapping(&quot;/api/upload&quot;)
    public ResponseEntity&lt;String&gt; upload(@RequestPart(name = &quot;file&quot;) MultipartFile file) throws IOException {
        return ResponseEntity.ok(s3UploadService.saveFile(file));
    }</code></pre>
<hr>
<h2 id="postman으로-테스트">postman으로 테스트</h2>
<p>form-data에서 File 형식으로 설정하고 컨트롤러에서 설정한 parameter name 을 넣어 준다.
업로드할 사진을 넣고 send!
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/59034556-8a9c-4139-acb9-74de77035112/image.png" alt="">
반환 값으로 업로드한 해당 사진의 url 값이 오게 됨</p>
<p>이제 이 url로 크롬에서 접속해 보면~?</p>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/1402a2f4-b395-469e-9b7f-2de58c0e0b21/image.png" alt=""></p>
<p>이미지 확인이 가능하다~</p>
<hr>
<h1 id="구성">구성</h1>
<p><img src="https://velog.velcdn.com/images/dumbveloper_100/post/d77dd910-44fc-4bdf-82f2-352a4a6d7832/image.png" alt="">
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/0efc2fc6-0eeb-4de1-be74-ad407fc23c33/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CloudWatch로 CPU 임계치 이메일 알람 설정]]></title>
            <link>https://velog.io/@dumbveloper_100/%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@dumbveloper_100/%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81</guid>
            <pubDate>Wed, 28 Feb 2024 02:59:10 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-bash"># 1단계
sudo apt install stress-ng

# 2단계
grep -c processor /proc/cpuinfo

# 3단계 
top

# 1코어의 CPU를 60초간 과부하로 설정, 70% 사용량으로 test( ctrl+z로 종료)
stress-ng --cpu 1 --cpu-load 73 --timeout 60s --metrics
# 하나의 CPU에 73%의 부하를 60초 동안 유지하면서 이에 대한 성능 메트릭을 표시
</code></pre>
<p>stress-ng: 시스템 리소스에 부하를 주는 리소스 스트레스 테스트 도구
--cpu 1: CPU 1개에 대한 부하 생성.
--cpu-load 73: CPU 부하의 크기, 73%의 부하를 생성.
--timeout 60s: 부하 테스트를 수행할 시간을 지정.
--metrics: 실행 중에 발생하는 메트릭(성능 측정 지표)를 표시. 이를 통해 CPU 사용률, 메모리 사용 등을 모니터링.</p>
<h1 id="cloudwatch">CloudWatch</h1>
<p>Amazon CloudWatch는 AWS 리소스와 AWS에서 실시간으로 실행 중인 애플리케이션을 모니터링 하는 서비스 입니다.</p>
<h3 id="cloudwatch에서-대시보드-만들고-위젯추가-cpu-utilization">CloudWatch에서 대시보드 만들고 위젯추가 (CPU Utilization)</h3>
<p>지표를 감시해 알림을 보내거나 임계값을 위반한 경우 모니터링 중인 리소스를 자동으로 변경하는 경보를 생성할 수 있습니다.</p>
<p>예를 들어 경보는 인스턴스 중지, auto scaling 및 Amazon SNS 작업 시작, 종료 등으로 구성할 수 있습니다. 
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/234101d9-14eb-4942-8093-f77d99a74e81/image.png" alt=""></p>
<h3 id="경보-생성">경보 생성</h3>
<p>경보 알림 메일을 빠르게 받아보기 위해 최대 , 1분 설정
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/0e19e4b1-1c06-4612-9e99-f07786ee9253/image.png" alt="">
조건 설정 , 임계값 50으로
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/98867d43-4f1e-40eb-a891-c21cf2fc23cf/image.png" alt="">
이메일 주소 입력
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/a4847167-2049-47d6-baf2-5d0618c6572c/image.png" alt="">
임계치를 넘게되면 메일이 전송됨 !
<img src="https://velog.velcdn.com/images/dumbveloper_100/post/8be1e744-0fb8-4956-b6da-9464dd49439e/image.png" alt=""></p>
<hr>
<p>메일 내용</p>
<pre><code>Alarm Details:
- Name:                       12myec2CPU알림
- Description:                *************
12myec2CPU알림
*************
- State Change:               INSUFFICIENT_DATA -&gt; ALARM
- Reason for State Change:    Threshold Crossed: 1 out of the last 1 datapoints [82.2033898305085 (28/02/24 02:38:00)] was greater than the threshold (50.0) (minimum 1 datapoint for OK -&gt; ALARM transition).
- Timestamp:                  Wednesday 28 February, 2024 02:43:59 UTC
- AWS Account:                646580111040
- Alarm Arn:                  arn:aws:cloudwatch:ap:alarm:12myec2CPU알림</code></pre><hr>
]]></description>
        </item>
    </channel>
</rss>