<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>delpho</title>
    <link>https://delpho.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 22 Aug 2026 18:09:13 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>delpho</managingEditor>
    <image>
      <title>delpho</title>
      <url>https://tistory1.daumcdn.net/tistory/4834669/attach/75af9ce549834f08ab7d6796149c0405</url>
      <link>https://delpho.tistory.com</link>
    </image>
    <item>
      <title>[실버1, DP] 백준 2156 - 포도주 시식 (자바)</title>
      <link>https://delpho.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. dp 문제를 읽고 항상 떠오르는 생각은, 이걸 DP로 어떻게 풀라는거지? 라는 생각이다... 아직 경험치가 많이 부족한가보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 완전탐색을 하지 않고 dp 점화식을 어떻게 세울 수 있을지 아이디어가 전혀 떠오르지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. dp[i]의 정의는, i번째 와인잔까지 고려했을때 마실 수 있는 최대 와인 양 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.점화식은 dp[i]=max(dp[i&amp;minus;1],dp[i&amp;minus;2]+wine[i],dp[i&amp;minus;3]+wine[i&amp;minus;1]+wine[i]) 다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. i번째 와인잔까지 고려했을때 마실 수 있는 최대 와인 양을 구하기 위해서는, 즉, dp[i]를 구하기 위해서는, 세가지 경우를 고려해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. (1) 현재 i번째의 와인잔을 마시지 않는 경우 (i-1번째까지의 마실 수 있는 최대 와인 양)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; (2) i-2번째까지의 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;마실 수 있는 최대 와인 양 + i번째 와인을 마시는 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; (3) &lt;/span&gt;i-3번째까지의&lt;span&gt;&amp;nbsp;&lt;/span&gt;마실 수 있는 최대 와인 양 + i-1번째 와인을 마시는 경우 + i-1번째 와인을 마시는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 이 세가지 경우만 고려한다면, 항상 최적의 최대 와인 양만 구할 수 있다.&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;/h2&gt;
&lt;pre id=&quot;code_1715904736918&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.*;

public class testtest {
    static int N, wine[];
    static Integer dp[];

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

    public static void main(String[] args) throws IOException {
        init();
        process();
    }


    private static void process() {

        dp[0] = 0;
        dp[1] = wine[1];

        if(N&amp;gt;=2){
            dp[2] = wine[1] + wine[2];
        }

        for (int i = 3; i &amp;lt;= N; i++) {
            dp[i] = Math.max(Math.max(dp[i-1], dp[i-2]+ wine[i]), dp[i-3] + wine[i-1] + wine[i]);
        }

        System.out.println(dp[N]);

    }


    private static void init() throws IOException {

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

        // dp[n] = n번째까지 먹을 수 있는 최대 와인의 양
        // dp는 하위 문제들의 결과를 어떻게 결합하여 상위 문제의 결과를 만들 수 있을지 생각
        // n == 3 : 1,5,3
        // n == 4 : 1,5,3,7
        // n == 5 : 1,5,3,7,2
        dp = new Integer[N + 1];
        wine = new int[N + 1];

        for (int i = 1; i &amp;lt;= N; i++) {
            wine[i] = Integer.parseInt(br.readLine());
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/DP</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/84</guid>
      <comments>https://delpho.tistory.com/84#entry84comment</comments>
      <pubDate>Fri, 17 May 2024 09:12:30 +0900</pubDate>
    </item>
    <item>
      <title>[실버3, DP] 백준 1904 - 01타일 (자바)</title>
      <link>https://delpho.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;DP 문제는 아직도 참 어렵당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;00과 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;&lt;a href=&quot;https://www.acmicpc.net/board/view/84734&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/board/view/84734&lt;/a&gt; 에서 letusshow님의 답변이 너무 좋았다. 해당 답변을 보니 이 문제의 풀이가 와닿았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타일을 만들 수 있는 경우는 2가지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 00 타일이 오는 경우 - 00...으로 시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 1 타일이 오는 경우 - 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;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;길이가 N인 이진 수열의 개수'를 f(N)이라고 정의하면 f(N) = f(N-1) + f(N-2)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;또한 f(0) = f(1) = 1임을 확인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;제출 코드&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1715499871022&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.*;

public class testtest {
    static int N;
    static Integer dp[];

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

    public static void main(String[] args) throws IOException {
        init();
        process();
    }

    private static void process() {
        System.out.println(dp(N));

    }

    private static int dp(int n) {

        if(dp[n] == null){
            dp[n] = (dp(n-2) + dp(n-1))  % 15746;
        }
        return dp[n];
    }

    private static void init() throws IOException {

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

        dp = new Integer[N+1];

        dp[0] = 0;
        dp[1] = 1;
        if(N &amp;gt;= 2) dp[2] = 2;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/DP</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/83</guid>
      <comments>https://delpho.tistory.com/83#entry83comment</comments>
      <pubDate>Sun, 12 May 2024 16:44:45 +0900</pubDate>
    </item>
    <item>
      <title>[골드2, BFS] 백준 16946 - 벽 부수고 이동하기 4 (자바)</title>
      <link>https://delpho.tistory.com/82</link>
      <description>&lt;h1&gt;Think&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;문제를 보고, 벽 위치를 큐에 다 넣고 isVisited[][][]를 통해 한번에 bfs 돌리는 방식으로 풀었다.&lt;/li&gt;
&lt;li&gt;하지만, 메모리 초과가 났고, 조건을 다시 보니 이해가 갔다.&lt;/li&gt;
&lt;li&gt;골드2 정도로 올라오니 BFS에 시간초과 혹은 메모리 초과가 걸리도록 조건을 주는거같다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://settembre.tistory.com/298&quot;&gt;https://settembre.tistory.com/298&lt;/a&gt; 를 참고하니 이해가 갔다. 생각보다 어려운 방식은 아니지만, 생각의 전환이 필요했다.&lt;/li&gt;
&lt;/ol&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;h1&gt;내 정답 코드&lt;/h1&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, M, map[][], resultMap[][];
    static boolean[][] isVisited;
    static Map&amp;lt;Integer, Integer&amp;gt; spaceMap = new HashMap&amp;lt;&amp;gt;();

    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static StringBuilder sb = new StringBuilder();
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;

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

        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; M; j++) {
                if (map[i][j] == 1) {
                    process(i, j);
                }
            }
        }

        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; M; j++) {
                sb.append(resultMap[i][j] % 10);
            }
            sb.append('\\n');
        }

        sb.delete(sb.length() - 1, sb.length());
        System.out.print(sb);
    }

    private static void process(int r, int c) {
        Set&amp;lt;Integer&amp;gt; set = new HashSet&amp;lt;&amp;gt;();
        int sum = 1;

        for (int i = 0; i &amp;lt; 4; i++) {
            int nextR = r + dr[i];
            int nextC = c + dc[i];

            if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= N || nextC &amp;gt;= M) continue;
            if (map[nextR][nextC] == 1) continue;
            if(set.contains(map[nextR][nextC])) continue;

            sum += spaceMap.get(map[nextR][nextC]);
            set.add(map[nextR][nextC]);
        }

        resultMap[r][c] = sum;
    }

    private static void init() throws IOException {
        st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];
        resultMap = new int[N][M];
        isVisited = new boolean[N][M];

        for (int i = 0; i &amp;lt; N; i++) {
            char[] inputs = br.readLine().toCharArray();

            for (int j = 0; j &amp;lt; M; j++) {
                map[i][j] = inputs[j] - '0';

                if(map[i][j] == 1){
                }
            }
        }

        int number = 2;
        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; M; j++) {
                if (map[i][j] == 0) {
                    bfs(i, j, number++);
                }
            }
        }
    }

    private static void bfs(int r, int c, int number) {
        Queue&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();
        int count = 1;

        q.add(new int[]{r, c});
        isVisited[r][c] = true;
        map[r][c] = number;

        while (!q.isEmpty()) {
            int[] currentPosition = q.poll();

            for (int i = 0; i &amp;lt; 4; i++) {
                int nextR = currentPosition[0] + dr[i];
                int nextC = currentPosition[1] + dc[i];

                if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= N || nextC &amp;gt;= M || isVisited[nextR][nextC]) continue;
                if (map[nextR][nextC] == 1) continue;

                q.add(new int[]{nextR, nextC});
                isVisited[nextR][nextC] = true;
                map[nextR][nextC] = number;
                count++;
            }
        }

        spaceMap.put(number, count);
    }
}

&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/82</guid>
      <comments>https://delpho.tistory.com/82#entry82comment</comments>
      <pubDate>Mon, 15 Apr 2024 23:03:36 +0900</pubDate>
    </item>
    <item>
      <title>[골드2, BFS] 백준 1525 - 퍼즐 (자바)</title>
      <link>https://delpho.tistory.com/81</link>
      <description>&lt;h1&gt;Think&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;30분정도 고민했는데 방법이 생각이 안났다. 규칙성이 있지 않을까 하면서 BFS를 활용해서 구현하는 방법을 고민했지만&amp;hellip; 안됐다&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loosie.tistory.com/253&quot;&gt;https://loosie.tistory.com/253&lt;/a&gt; 를 참고했다.&lt;/li&gt;
&lt;li&gt;0을 기준으로 BFS하면서 완전탐색을 하는 방식이었다.&lt;/li&gt;
&lt;li&gt;0이 상하좌우 움직일때의 모든 경우의 수를 탐색해야하는데, boolean[][] 배열로는 불가능? 비효율?적이기 때문에 HashMap을 사용하여 중복체크를 한다.&lt;/li&gt;
&lt;li&gt;또한, input을 2차원배열로 저장하지 않고, String으로 관리한다. 탐색할때는 나머지연산, 나누기 연산을 활용해서 위치를 관리한다.&lt;/li&gt;
&lt;li&gt;이렇게 새로운 풀이방식이 나때마다 어떻게 이런 생각을 할까 하면서 감탄하게 된다 ㅎ.ㅎ..&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;내 정답 코드&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static String puzzle = &quot;&quot;;
    static String correctPuzzle = &quot;123456780&quot;;
    static StringBuilder newPuzzle;
    static Map&amp;lt;String, Integer&amp;gt; isVisitedMap = new HashMap&amp;lt;&amp;gt;();
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};

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

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

        process();
    }

    private static void process() {

        Queue&amp;lt;String&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();

        q.add(puzzle); 
        isVisitedMap.put(puzzle , 0);

        while(!q.isEmpty()){
            puzzle = q.poll();

            int zeroIndex = puzzle.indexOf(&quot;0&quot;);
            int zeroR = zeroIndex / 3;
            int zeroC = zeroIndex % 3;

            if(correctPuzzle.equals(puzzle)) {
                System.out.println(isVisitedMap.get(puzzle));
                return;
            }

            for (int i = 0; i &amp;lt; 4; i++) {
                int nextZeroR = zeroR + dr[i];
                int nextZeroC = zeroC + dc[i];

                if(nextZeroR &amp;lt; 0 || nextZeroC &amp;lt; 0 || nextZeroR &amp;gt;= 3 || nextZeroC &amp;gt;= 3)  continue;

                int nextZeroIndex = nextZeroR * 3 + nextZeroC;

                newPuzzle = new StringBuilder(puzzle);
                char tempChar = newPuzzle.charAt(nextZeroIndex);
                newPuzzle.setCharAt(nextZeroIndex, '0');
                newPuzzle.setCharAt(zeroR * 3 + zeroC, tempChar);

                if(!isVisitedMap.containsKey(newPuzzle.toString())){
                    q.add(newPuzzle.toString());
                    isVisitedMap.put(newPuzzle.toString(), isVisitedMap.get(puzzle) + 1);

                }
            }
        }

        System.out.println(-1);
    }

    private static void init() throws IOException {

        for (int i = 0; i &amp;lt; 3; i++) {
            puzzle += br.readLine().replaceAll(&quot; &quot;, &quot;&quot;);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/81</guid>
      <comments>https://delpho.tistory.com/81#entry81comment</comments>
      <pubDate>Fri, 12 Apr 2024 16:13:55 +0900</pubDate>
    </item>
    <item>
      <title>[골드3, BFS] 백준 1600 - 말이 되고픈 원숭이 (자바)</title>
      <link>https://delpho.tistory.com/80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Think&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;4방탐색과 말 움직임을 구현하는건 어렵진 않았다. 다만 오른쪽 아래까지 가는 최단거리를 어떻게 구할지가 고민이었다.&lt;/li&gt;
&lt;li&gt;처음에는 무조건 말 움직임으로 가는것이 최단거리이지 않을까 생각했다.&lt;/li&gt;
&lt;li&gt;하지만, 탐색하려는 특정 정점까지의 최단거리는 무조건 말 움직임으로 갈때만이 최단 거리가 아니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1&lt;br /&gt;5 5&lt;br /&gt;00010 &lt;br /&gt;00010 &lt;br /&gt;00110 &lt;br /&gt;01110 &lt;br /&gt;00000&lt;/li&gt;
&lt;li&gt;예를들어, 위의 인풋이 들어왔을떄, [5,2]까지 가는 최단거리는 3가지 방법이 있다.&lt;/li&gt;
&lt;li&gt;4방탐색으로만 가기 (6번) / 말 움직임 먼저 간 후[2,1], 4방탐색으로 가기 (6번) / 4방탐색으로 먼저 간 후[2,1], 말 움직임으로 가기 (4번)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그렇기 때문에, 그리디로 풀수는 없고 완탐으로 풀어야된다는 생각을 했다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yabmoons.tistory.com/48&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yabmoons.tistory.com/48&lt;/a&gt; 에서도 잘 설명되어있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;BFS를 여러번해서는 시간초과가 날 수 있기때문에, 한번의 BFS로 해야한다는 생각이 들었고, &lt;a href=&quot;https://delpho.tistory.com/76&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백준 2206 - 벽 부수고 이동하기&lt;/a&gt; 문제를 풀때의 방법이 생각나서 이 방법으로 풀었다.&lt;/li&gt;
&lt;li&gt;99%정도에서 틀렸길래 예외를 생각해봤는데, W와 H가 1로 들어올수있다. 그렇기때문에 이 경우를 예외처리 해주자.&lt;/li&gt;
&lt;/ol&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;/h2&gt;
&lt;pre id=&quot;code_1712892203346&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.*;

public class test {
    static int K, W, H, map[][], result;
    static boolean isVisited[][][];
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static int[] horseDr = new int[]{-2, -1, 1, 2, 2, 1, -1, -2};
    static int[] horseDc = new int[]{1, 2, 2, 1, -1, -2, -2, -1};

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

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

        process();

        if(result == 0) {
            if(W == 1 &amp;amp;&amp;amp; H == 1) System.out.println(0);
            else System.out.println(-1);
        }
        else System.out.println(result);
    }

    private static void process() {

        bfs();

    }

    private static void bfs() {
        Queue&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();
        isVisited = new boolean[K+1][H][W];

        q.add(new int[]{0, 0, 0, 0});
        isVisited[0][0][0] = true;

        while (!q.isEmpty()) {
            int[] currentPosition = q.poll();

            if (currentPosition[0] == H - 1 &amp;amp;&amp;amp; currentPosition[1] == W - 1) {
                result = currentPosition[3];
                break;
            }

            for (int i = 0; i &amp;lt; 4; i++) {
                int nextR = currentPosition[0] + dr[i];
                int nextC = currentPosition[1] + dc[i];
                int nextSpendK = currentPosition[2]; // 4방탐색일때는 K사용X
                int nextMoveCount = currentPosition[3] + 1;

                if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= H || nextC &amp;gt;= W || isVisited[nextSpendK][nextR][nextC]) continue;
                if(map[nextR][nextC] == 1) continue;

                q.add(new int[]{nextR, nextC, nextSpendK, nextMoveCount});
                isVisited[nextSpendK][nextR][nextC] = true;
            }

            for (int i = 0; i &amp;lt; 8; i++) {
                int nextR = currentPosition[0] + horseDr[i];
                int nextC = currentPosition[1] + horseDc[i];
                int nextSpendK = currentPosition[2] + 1;
                int nextMoveCount = currentPosition[3] + 1;

                if (nextSpendK &amp;gt;= K+1) continue;
                if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= H || nextC &amp;gt;= W || isVisited[nextSpendK][nextR][nextC]) continue;
                if(map[nextR][nextC] == 1) continue;

                q.add(new int[]{nextR, nextC, nextSpendK, nextMoveCount});
                isVisited[nextSpendK][nextR][nextC] = true;
            }
        }
    }

    private static void init() throws IOException {
        K = Integer.parseInt(br.readLine());

        st = new StringTokenizer(br.readLine());
        W = Integer.parseInt(st.nextToken());
        H = Integer.parseInt(st.nextToken());

        map = new int[H][W];

        for (int i = 0; i &amp;lt; H; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 0; j &amp;lt; W; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/80</guid>
      <comments>https://delpho.tistory.com/80#entry80comment</comments>
      <pubDate>Fri, 12 Apr 2024 12:23:59 +0900</pubDate>
    </item>
    <item>
      <title>[골드3, BFS] 백준 2638 - 치즈 (자바)</title>
      <link>https://delpho.tistory.com/79</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Think&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;문제를 딱 봤을때 엄청 어려워보이진 않았다. 내부공기, 외부공기 구분 하는 것만 신경써주면 될거같았다.&lt;/li&gt;
&lt;li&gt;가장자리는 무조건 치즈가 없으니, 0,0 BFS돌려서 외부공기로 넘버링하고, 치즈 탐색하면서 외부공기와 맞닿은 개수 체크한다음에, 녹은 치즈를 다시 외부공기로 넘버링 하는 방식으로 구현&lt;/li&gt;
&lt;li&gt;녹은 치즈를 바로 외부공기로 넘버링하면, 다음 탐색하는 치즈가 접촉하는 외부 공기의 수가 +1 되어버리니, 일단은 0으로 변환한다음에 이후 외부공기로 수정&amp;nbsp;&lt;/li&gt;
&lt;/ol&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;/h2&gt;
&lt;pre id=&quot;code_1712886977986&quot; class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, M, map[][],countCheeze;
    static boolean isVisited[][];
    static Queue&amp;lt;int[]&amp;gt; meltingCheezePosition = new ArrayDeque&amp;lt;&amp;gt;();
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;


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

        process();

    }

    private static void process() {
        int second = 0;

        // 외부 공기를 -1로 넘버링
        numberingOutsideBFS(0, 0);

        // 치즈 개수가 0이 될때까지 반복
        while (countCheeze != 0) {

            // 치즈가 외부 공기와 접촉한 면이 2개 이상인지 체크 후, 해당 위치는 0으로 수정
            for (int i = 0; i &amp;lt; N; i++) {
                for (int j = 0; j &amp;lt; M; j++) {
                    if (map[i][j] == 1 &amp;amp;&amp;amp; isContactedTwoOutside(i, j)) {
                        map[i][j] = 0;
                        meltingCheezePosition.add(new int[]{i,j});
                        countCheeze--;
                    }
                }
            }

            // 0으로 수정한 치즈를 -1로 수정하기
            int size = meltingCheezePosition.size();
            for (int i = 0; i &amp;lt; size; i++) {
                int[] p = meltingCheezePosition.poll();
                if(map[p[0]][p[1]] == 0){
                    numberingOutsideBFS(p[0], p[1]);
                }
            }

            second++;
        }

        System.out.println(second);
    }

    private static boolean isContactedTwoOutside(int r, int c) {
        int countOutside = 0;

        for (int i = 0; i &amp;lt; 4; i++) {
            int nextR = r + dr[i];
            int nextC = c + dc[i];

            if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= N || nextC &amp;gt;= M) continue;

            if(map[nextR][nextC] == -1){
                countOutside++;
            }
        }

        if(countOutside &amp;gt;= 2) return true;
        else return false;
    }

    private static void numberingOutsideBFS(int r, int c) {
        Queue&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();

        isVisited = new boolean[N][M];
        isVisited[r][c] = true;
        q.add(new int[]{r, c});
        map[r][c] = -1;

        while (!q.isEmpty()) {
            int[] currentRC = q.poll();

            for (int i = 0; i &amp;lt; 4; i++) {
                int nextR = currentRC[0] + dr[i];
                int nextC = currentRC[1] + dc[i];

                if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= N || nextC &amp;gt;= M || isVisited[nextR][nextC]) continue;

                if(map[nextR][nextC] == 0){
                    map[nextR][nextC] = -1;
                    q.add(new int[]{nextR,nextC});
                    isVisited[nextR][nextC] = true;
                }
            }
        }

    }

    private static void init() throws IOException {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];

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

            for (int j = 0; j &amp;lt; M; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());

                if(map[i][j] == 1)  countCheeze++;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1712886978764&quot; class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, M, map[][],countCheeze;
    static boolean isVisited[][];
    static Queue&amp;lt;int[]&amp;gt; meltingCheezePosition = new ArrayDeque&amp;lt;&amp;gt;();
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;


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

        process();

    }

    private static void process() {
        int second = 0;

        // 외부 공기를 -1로 넘버링
        numberingOutsideBFS(0, 0);

        // 치즈 개수가 0이 될때까지 반복
        while (countCheeze != 0) {

            // 치즈가 외부 공기와 접촉한 면이 2개 이상인지 체크 후, 해당 위치는 0으로 수정
            for (int i = 0; i &amp;lt; N; i++) {
                for (int j = 0; j &amp;lt; M; j++) {
                    if (map[i][j] == 1 &amp;amp;&amp;amp; isContactedTwoOutside(i, j)) {
                        map[i][j] = 0;
                        meltingCheezePosition.add(new int[]{i,j});
                        countCheeze--;
                    }
                }
            }

            // 0으로 수정한 치즈를 -1로 수정하기
            int size = meltingCheezePosition.size();
            for (int i = 0; i &amp;lt; size; i++) {
                int[] p = meltingCheezePosition.poll();
                if(map[p[0]][p[1]] == 0){
                    numberingOutsideBFS(p[0], p[1]);
                }
            }

            second++;
        }

        System.out.println(second);
    }

    private static boolean isContactedTwoOutside(int r, int c) {
        int countOutside = 0;

        for (int i = 0; i &amp;lt; 4; i++) {
            int nextR = r + dr[i];
            int nextC = c + dc[i];

            if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= N || nextC &amp;gt;= M) continue;

            if(map[nextR][nextC] == -1){
                countOutside++;
            }
        }

        if(countOutside &amp;gt;= 2) return true;
        else return false;
    }

    private static void numberingOutsideBFS(int r, int c) {
        Queue&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();

        isVisited = new boolean[N][M];
        isVisited[r][c] = true;
        q.add(new int[]{r, c});
        map[r][c] = -1;

        while (!q.isEmpty()) {
            int[] currentRC = q.poll();

            for (int i = 0; i &amp;lt; 4; i++) {
                int nextR = currentRC[0] + dr[i];
                int nextC = currentRC[1] + dc[i];

                if (nextR &amp;lt; 0 || nextC &amp;lt; 0 || nextR &amp;gt;= N || nextC &amp;gt;= M || isVisited[nextR][nextC]) continue;

                if(map[nextR][nextC] == 0){
                    map[nextR][nextC] = -1;
                    q.add(new int[]{nextR,nextC});
                    isVisited[nextR][nextC] = true;
                }
            }
        }

    }

    private static void init() throws IOException {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];

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

            for (int j = 0; j &amp;lt; M; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());

                if(map[i][j] == 1)  countCheeze++;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/79</guid>
      <comments>https://delpho.tistory.com/79#entry79comment</comments>
      <pubDate>Fri, 12 Apr 2024 11:01:50 +0900</pubDate>
    </item>
    <item>
      <title>[골드3, BFS] 백준 2146 - 다리 만들기 (자바)</title>
      <link>https://delpho.tistory.com/78</link>
      <description>&lt;h1&gt;Think&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;문제를 처음 읽고나서 생각한 풀이는, 섬 가장자리 위치를 큐에 모두 저장하고, BFS를 한번에 돌리면 되겠다고 생각했다.&lt;/li&gt;
&lt;li&gt;하지만, 가장자리 포인트들에서 동시에 BFS가 돌아가다보니, 거리가 상쇄가 되어버렸다. (거리가 4라면 2로 되어버림)&lt;/li&gt;
&lt;li&gt;어떻게 풀지 계속 고민하다가 구글링을 했다. (&lt;a href=&quot;https://todaycode.tistory.com/166&quot;&gt;https://todaycode.tistory.com/166&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;풀이 방식은 이렇다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;각 섬들을 넘버링한다. (다른 섬의 경계에 도착했는지 체크하기 위함, 1로만 되어있는 섬 표식을 2,3,4로 표시 (2부터 넘버링하는게 더 편함))&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;2중 for문을 돌면서 BFS를 시도한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;map[nextR][nextC] &amp;ne; 0 이면, 섬 가장자리가 아니라는 의미이기 때문에 BFS를 진행하지 않는다. (다른 if문으로 인해 자동으로 걸러짐)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;map[nextR][nextC] == 0 이면, BFS 진행&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;map[nextR][nextC] &amp;ne; islandNumber 이면, 다른 섬에 도착했다는 의미 (최소 거리 체크 하기)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;q.add(new int[]{r,c,0}); 와 같이, distance를 큐에 같이 넣어주기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;if(position[2] &amp;gt;= *minDistance*) continue; 조건을 통해 가지치기 진행 (속도 차이 6배정도..)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPH6pE/btsGwhAXjwl/TxBt6vPGGdHzUkogaBBNAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPH6pE/btsGwhAXjwl/TxBt6vPGGdHzUkogaBBNAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPH6pE/btsGwhAXjwl/TxBt6vPGGdHzUkogaBBNAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPH6pE%2FbtsGwhAXjwl%2FTxBt6vPGGdHzUkogaBBNAk%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;832&quot; height=&quot;151&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;151&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;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;정답 코드&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, map[][], minDistance = Integer.MAX_VALUE;
    static boolean isVisited[][];
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;

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

        process();

        System.out.println(minDistance);
    }

    private static void process() {

        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; N; j++) {
                if(map[i][j] != 0){
                    bfs(i,j, map[i][j]);
                }
            }
        }

    }

    private static void bfs(int r, int c, int islandNumber) {
        Deque&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();

        isVisited = new boolean[N][N];

        isVisited[r][c] = true;
        q.add(new int[]{r,c,0});

        while (!q.isEmpty()) {
            int[] position = q.poll();
            if(position[2] &amp;gt;= minDistance) continue;

            for (int i = 0; i &amp;lt; 4; i++) {
                int mr = position[0] + dr[i];
                int mc = position[1] + dc[i];
                int mDistance = position[2] + 1;

                if (mr &amp;lt; 0 || mc &amp;lt; 0 || mr &amp;gt;= N || mc &amp;gt;= N || isVisited[mr][mc]) continue;

                if(map[mr][mc] == 0){
                    q.add(new int[]{mr, mc, mDistance});
                    isVisited[mr][mc] = true;
                }else if(map[mr][mc] != islandNumber){
                    minDistance = Math.min(minDistance, mDistance - 1);
                }
            }
        }
    }

    private static void init() throws IOException {
        N = Integer.parseInt(br.readLine());

        map = new int[N][N];

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

            for (int j = 0; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        int labelNumber = 2;

        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; N; j++) {
                if(map[i][j] == 1){
                    islandLabelingBFS(i,j, labelNumber);
                    labelNumber++;
                }
            }
        }
    }

    private static void islandLabelingBFS(int r, int c, int labelNumber) {
        Deque&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();

        isVisited = new boolean[N][N];
        isVisited[r][c] = true;
        map[r][c] = labelNumber;

        q.add(new int[]{r,c});

        while (!q.isEmpty()) {
            int[] position = q.poll();

            for (int i = 0; i &amp;lt; 4; i++) {
                int mr = position[0] + dr[i];
                int mc = position[1] + dc[i];

                if (mr &amp;lt; 0 || mc &amp;lt; 0 || mr &amp;gt;= N || mc &amp;gt;= N || isVisited[mr][mc] || map[mr][mc] == 0) continue;

                map[mr][mc] = labelNumber;
                q.add(new int[]{mr, mc});
                isVisited[mr][mc] = true;
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;처음 시도했다가 틀린 코드&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, map[][], distanceMap[][], minDistance = Integer.MAX_VALUE;
    static LinkedList&amp;lt;int[]&amp;gt; boundaryLands = new LinkedList&amp;lt;&amp;gt;();
    static Deque&amp;lt;int[]&amp;gt; q = new ArrayDeque&amp;lt;&amp;gt;();
    static Deque&amp;lt;int[]&amp;gt; distanceQ = new ArrayDeque&amp;lt;&amp;gt;();
    static boolean isVisited[][];
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;

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

        process();

        for (int i = 0; i &amp;lt; N; i++) {
            System.out.println(Arrays.toString(distanceMap[i]));
        }
        System.out.println(minDistance);
    }

    private static void process() {
        bfs();

        for(int[] p : boundaryLands){
            minDistance = Math.min(distanceMap[p[0]][p[1]], minDistance);
        }
    }

    private static void bfs() {

        isVisited = new boolean[N][N];
        int distance = 0;

        while(!q.isEmpty()){
            int qSize = q.size();
            for (int i = 0; i &amp;lt; qSize; i++) {
                int[] position = q.poll();
                isVisited[position[0]][position[1]] = true;

                for (int j = 0; j &amp;lt; 4; j++) {
                    int mr = position[0] + dr[j];
                    int mc = position[1] + dc[j];

                    if(mr&amp;lt;0 || mc&amp;lt;0||mr&amp;gt;=N || mc&amp;gt;= N || isVisited[mr][mc] || map[mr][mc] == 1)   continue;

                    q.add(new int[]{mr,mc});
                    distanceQ.add(new int[]{mr,mc});
                    isVisited[mr][mc] = true;
                }
            }

            distance++;

            while(!distanceQ.isEmpty()){
                int size = distanceQ.size();

                for (int i = 0; i &amp;lt; size; i++) {
                    int[] p = distanceQ.poll();
                    distanceMap[p[0]][p[1]] = distance;
                }
            }
        }
    }

    private static void init() throws IOException {
        N = Integer.parseInt(br.readLine());

        map = new int[N][N];
        distanceMap = new int[N][N];

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

            for (int j = 0; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; N; j++) {
                // 섬 가장자리 부분을 찾아서 해당 r,c를 큐에 저장
                if(map[i][j] == 1){

                    for (int k = 0; k &amp;lt; 4; k++) {
                        int mr = i + dr[k];
                        int mc = j + dc[k];

                        if(mr&amp;lt;0 || mc&amp;lt;0||mr&amp;gt;=N || mc&amp;gt;= N)   continue;

                        if(map[mr][mc] == 0){
                            boundaryLands.add(new int[]{i,j});
                            q.add(new int[]{i,j});
                            break;
                        }
                    }
                }
            }
        }

    }

}

&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/78</guid>
      <comments>https://delpho.tistory.com/78#entry78comment</comments>
      <pubDate>Thu, 11 Apr 2024 14:43:40 +0900</pubDate>
    </item>
    <item>
      <title>[골드3, BFS+PQ] 백준 16236 - 아기 상어 (자바)</title>
      <link>https://delpho.tistory.com/77</link>
      <description>&lt;h1&gt;Think&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;물고기를 어떤 자료구조로 관리할지, 상어기준으로 물고기와의 거리를 어떻게 계산할지, 먹는 우선순위를 어떻게 구분할지에 대해서 먼저 고민하고 시작했다.&lt;/li&gt;
&lt;li&gt;물고기는 Fish라는 클래스를 만들고 LinkedList로 관리 / 상어와 물고기와의 거리는 BFS를 통해 계산 (한번 먹을때마다 다시 계산) / 우선순위는 Fish 클래스에 Comparable을 통해 정렬시키기 - 를 통해 구현했다&lt;/li&gt;
&lt;li&gt;물고기 먹는 순서와 상어가 갇혔을때의 경우를 고려하지못해서 약간 정체됐지만, 그래도 풀었다.&lt;/li&gt;
&lt;li&gt;188ms로 통과했길래 다른 사람은 얼마나 걸렸는지 봤는데 84ms로 통과한 사람이 있어서 풀이를 구경했다.&lt;/li&gt;
&lt;li&gt;나는 상어가 물고기를 먹을때마다 물고기와의 거리를 계속 BFS를 통해 계산했는데, 이 사람은 BFS 한번만으로 풀어냈다. 그 아이디어는 우선순위 큐였다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jouureee.tistory.com/88&quot;&gt;https://jouureee.tistory.com/88&lt;/a&gt; 를 참고하여 풀이를 이해했다.&lt;/li&gt;
&lt;/ol&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;h1&gt;내 정답코드 (BFS 한번 + PQ 활용)&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, maxSecond;
    static int[][] map;
    static boolean[][] isVisited;
    static Shark shark;
    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;

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

        process();

        System.out.println(maxSecond);
    }

    private static void process() {
        bfsAndEatFish();
    }

    private static void bfsAndEatFish() {

        Queue&amp;lt;int[]&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        isVisited = new boolean[N][N];

        q.add(new int[]{shark.r, shark.c});
        isVisited[shark.r][shark.c] = true;

        PriorityQueue&amp;lt;Fish&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();
        int distance = 0;

        while(!q.isEmpty()){
            int qSize = q.size();

            for (int i = 0; i &amp;lt; qSize; i++) {
                int[] sharkPosition = q.poll();

                for (int j = 0; j &amp;lt; 4; j++) {
                    int mr = sharkPosition[0] + dr[j];
                    int mc = sharkPosition[1] + dc[j];

                    if(mr &amp;lt; 0 || mc &amp;lt; 0 || mr &amp;gt;= N || mc &amp;gt;= N || isVisited[mr][mc]) continue;

                    if(map[mr][mc] != 0 &amp;amp;&amp;amp; map[mr][mc] &amp;lt; shark.size){
                        pq.add(new Fish(mr,mc,map[mr][mc]));
                    }
                    if(map[mr][mc] &amp;lt;= shark.size){
                        q.add(new int[]{mr,mc});
                        isVisited[mr][mc] = true;
                    }
                }
            }

            distance++;

            if(!pq.isEmpty()){
                Fish fish = pq.poll();
                shark.countEattingFish++;
                map[fish.r][fish.c] = 0; //물고기 먹었으니 그 자리는 0으로 만들기

                if(shark.countEattingFish == shark.size){
                    shark.size++;
                    shark.countEattingFish = 0;
                }

                maxSecond += distance; // 물고기 위치까지 간 시간 더해주기
                distance = 0;
                pq.clear();
                q.clear();
                isVisited = new boolean[N][N];
                q.add(new int []{fish.r, fish.c});
                isVisited[fish.r][fish.c] = true;
            }
        }
    }

    private static void init() throws IOException {

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

        map = new int[N][N];

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

            for (int j = 0; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());

                if (map[i][j] == 9) {
                    shark = new Shark(i, j, 2, 0);
                    map[i][j] = 0;
                }
            }
        }
    }

    public static class Fish implements Comparable&amp;lt;Fish&amp;gt; {
        int r;
        int c;
        int size;

        public Fish(int r, int c, int size) {
            this.r = r;
            this.c = c;
            this.size = size;
        }

        @Override
        public int compareTo(Fish o) {
            int res = 0;

            if (this.r == o.r) {
                res = this.c - o.c;
            } else {
                res = this.r - o.r;
            }
            return res;
        }

    }

    public static class Shark {
        int r;
        int c;
        int size;
        int countEattingFish;

        public Shark(int r, int c, int size, int countEattingFish) {
            this.r = r;
            this.c = c;
            this.size = size;
            this.countEattingFish = countEattingFish;
        }
    }

}

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;내 정답코드 (물고기 먹을때마다 BFS)&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class test {
    static int N, maxSecond;
    static int[][] map;
    static int[][] distanceMap;
    static boolean[][] isVisited;
    static Shark shark;
    static LinkedList&amp;lt;Fish&amp;gt; fish = new LinkedList&amp;lt;&amp;gt;();

    static int[] dr = new int[]{0, 0, 1, -1};
    static int[] dc = new int[]{1, -1, 0, 0};
    static StringBuilder sb = new StringBuilder();
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;

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

        process();

        System.out.println(maxSecond);
    }

    private static void process() {

        while(fish.size() != 0){
            calculateDistance();

            int index = -1;
            for (int i = 0; i &amp;lt; fish.size(); i++) {
                if(fish.get(i).size &amp;lt; shark.size &amp;amp;&amp;amp; fish.get(i).distanceToShark != Integer.MAX_VALUE) {
                    index = i;
                    break;
                }
            }

            if(index == -1 ) break;
            Fish f = fish.remove(index);

            map[shark.r][shark.c] = 0;
            shark.r = f.r;
            shark.c = f.c;
            map[f.r][f.c] = 9;
            shark.countEattingFish++;
            maxSecond += f.distanceToShark;

            if(shark.countEattingFish == shark.size){
                shark.size++;
                shark.countEattingFish = 0;
            }
        }
    }

    private static void calculateDistance() {

        Queue&amp;lt;int[]&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        isVisited = new boolean[N][N];
        distanceMap = new int[N][N];

        q.add(new int[]{shark.r, shark.c});
        isVisited[shark.r][shark.c] = true;
        distanceMap[shark.r][shark.c] = 0;

        while(!q.isEmpty()){
            int[] position = q.poll();

            for (int i = 0; i &amp;lt; 4; i++) {
                int mr = position[0] + dr[i];
                int mc = position[1] + dc[i];

                if(mr &amp;lt; 0 || mc &amp;lt; 0 || mr &amp;gt;= N || mc &amp;gt;= N || isVisited[mr][mc]) continue;
                if(map[mr][mc] &amp;gt; shark.size)    continue;

                distanceMap[mr][mc] = distanceMap[position[0]][position[1]] + 1;
                q.add(new int[]{mr,mc});
                isVisited[mr][mc] = true;
            }
        }

        for(Fish f : fish){
            if(distanceMap[f.r][f.c] == 0){
                f.distanceToShark = Integer.MAX_VALUE;
            }else{
                f.distanceToShark = distanceMap[f.r][f.c];
            }
        }

        Collections.sort(fish);
    }

    private static void init() throws IOException {

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

        map = new int[N][N];

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

            for (int j = 0; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());

                if (map[i][j] == 9) {
                    shark = new Shark(i, j, 2, 0);
                } else if (map[i][j] != 0) {
                    Fish f = new Fish(i, j, map[i][j]);
                    fish.add(f);
                }
            }
        }
    }

    public static class Fish implements Comparable&amp;lt;Fish&amp;gt; {
        int r;
        int c;
        int size;
        int distanceToShark;

        public Fish(int r, int c, int size) {
            this.r = r;
            this.c = c;
            this.size = size;
        }

        @Override
        public int compareTo(Fish o1) {
            int res = 0;

            if (this.distanceToShark != o1.distanceToShark) {
                res = this.distanceToShark - o1.distanceToShark;
            }else if (this.r != o1.r) {
                res = this.r - o1.r;
            } else {
                res = this.c - o1.c;
            }
            return res;
        }

    }

    public static class Shark {
        int r;
        int c;
        int size;
        int countEattingFish;

        public Shark(int r, int c, int size, int countEattingFish) {
            this.r = r;
            this.c = c;
            this.size = size;
            this.countEattingFish = countEattingFish;
        }
    }

}

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;84ms의 정답코드 (참고한 코드)&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static int N;
    static int[][] map;
    static boolean[][] visited;
    static Queue&amp;lt;Point&amp;gt; queue;
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        N = Integer.parseInt(br.readLine());
        map = new int[N][N];

        queue = new ArrayDeque&amp;lt;&amp;gt;();
        visited = new boolean[N][N];

        for (int i = 0; i &amp;lt; N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j &amp;lt; N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if (map[i][j] == 9) {
                    queue.add(new Point(i, j));  // 아기상어 초기 위치, 크기:2
                    map[i][j] = 0;  // 아기 상어 위치 빈칸으로 바꿔주기
                    visited[i][j] = true;
                }
            }
        }
        bfs();
        System.out.println(result);
    }
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static int result;

    public static void bfs(){
        int sharkSize = 2;  // 상어 크기
        int feedCnt = 0;    // 먹이 먹은 개수
        result = 0;     // 결과

        PriorityQueue&amp;lt;Point&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();
        int round = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();

            for (int s = 0; s &amp;lt; size; s++) {
                Point shark = queue.poll();

                for (int d = 0; d &amp;lt; 4; d++) {

                    int nexti = shark.i + dx[d];
                    int nextj = shark.j + dy[d];

                    if (nexti &amp;gt;= 0 &amp;amp;&amp;amp; nexti &amp;lt; N &amp;amp;&amp;amp; nextj &amp;gt;= 0 &amp;amp;&amp;amp; nextj &amp;lt; N &amp;amp;&amp;amp; !visited[nexti][nextj]) {
                        // map 범위 안일 때
                        if ( map[nexti][nextj] != 0 &amp;amp;&amp;amp; map[nexti][nextj] &amp;lt; sharkSize) {
                            pq.add(new Point(nexti, nextj));
                        }
                        if (map[nexti][nextj] &amp;lt;= sharkSize) {
                            queue.add(new Point(nexti, nextj));
                            visited[nexti][nextj] = true;
                        }
                    }
                }
            }
            round++;
            // pq가 비어있지않다면?
            if (!pq.isEmpty()) {
//                System.out.println(pq);
//                System.out.println(&quot;round : &quot; + round);
                Point feed = pq.poll();
                feedCnt++;
                map[feed.i][feed.j] = 0; // 먹이 먹었으니까 빈칸으로 만들어주고

                if (feedCnt == sharkSize) {
                    sharkSize++;
                    feedCnt = 0;
                }
                result = round;
//                System.out.println(&quot;result : &quot; + result);
                pq.clear();
                queue.clear();
                visited = new boolean[N][N];
                queue.add(new Point(feed.i, feed.j));
                visited[feed.i][feed.j] = true;
            }
        }
    }
    public static class Point implements Comparable&amp;lt;Point&amp;gt; {
        int i, j;

        public Point(int i, int j) {
            this.i = i;
            this.j = j;
        }

        @Override
        public String toString() {
            return &quot;Point{&quot; +
                    &quot;i=&quot; + i +
                    &quot;, j=&quot; + j +
                    '}';
        }

        // 가장 위에 물고기 선택 but 그런 물고기 많다면 가장 왼쪽 물고기 냠
        @Override
        public int compareTo(Point o) {
            if (this.i == o.i) {
                return this.j - o.j;
            }
            else return this.i - o.i;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/77</guid>
      <comments>https://delpho.tistory.com/77#entry77comment</comments>
      <pubDate>Thu, 11 Apr 2024 11:03:03 +0900</pubDate>
    </item>
    <item>
      <title>[골드3, BFS] 백준 2206 - 벽 부수고 이동하기 (자바)</title>
      <link>https://delpho.tistory.com/76</link>
      <description>&lt;h1&gt;Think&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;처음에는 벽 위치를 저장해두고, 하나씩 벽을 없애면서 모든 경우의 수를 dfs 했다.&lt;/li&gt;
&lt;li&gt;그런데&amp;hellip; 알고보니 시간복잡도가 NM^2였다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kscodebase.tistory.com/66를&quot;&gt;https://kscodebase.tistory.com/66를&lt;/a&gt; 참고했다.&lt;/li&gt;
&lt;li&gt;int[][][] isVisited를 활용하여, 벽을 뿌신 경우와 안뿌신 경우를 구분해서 관리할 수 있었다.&lt;/li&gt;
&lt;li&gt;DFS는 가중치가 없을 경우(다익스트라를 사용하지않아도 되는 경우), 무조건 최단거리를 보장한다.&lt;/li&gt;
&lt;li&gt;또한, Queue 특성상, 벽을 뚫고 가는 경우와 안뚫고 돌아가는 경우 모두 체크가 가능하다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 개념이 이 문제를 푸는 아이디어를 이해하는데에 중요한 포인트라고 생각한다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜냐면&amp;hellip;.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;벽을 안 부수고도 현재 칸까지 도달이 가능하지만, 벽을 부수고 오는 것이 더 짧다고 가정해봅시다. 현재 지점에서 목표 지점까지 가려면 무조건 벽을 한 개 부숴야만 된다고 해봅시다. 비록 현재 칸까지는 벽을 부수고 오는 것이 최적이었지만, 이 상태로는 끝에 아예 도달을 못 하죠? 현재 칸까지는 더 멀더라도 벽을 안 부수고 와야, 끝에 도달이 가능합니다.&lt;/li&gt;
&lt;li&gt;현재 칸까지 벽을 안 부수고 최단으로 올 수 있었다고 가정해봅시다. 현재 지점에서 목표 지점까지 가는 데에, 벽을 한 개 부수고 가는 것이 안 부수고 가는 것보다 최적이 나온다고 해봅시다. 그렇다면 지금 내가 벽을 더 부술 수 있는 상태라는 사실을 알고 있어야만 할 것입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;라는 케이스까지 다 탐색할 수 있기 떄문!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;if(isVisited[position.breakingFlag][mr][mc] &amp;gt; 0) continue; 의 조건도 중요하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BFS를 통해 특정 지점에 왔다는건 최단 거리로 왔다는 의미이기 때문에, breakingFlag가 어떤 상태이든 이미 들른 곳이라면 패스!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;/h2&gt;
&lt;pre id=&quot;code_1712651423914&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.*;

public class test {
    static int N, M, result;
    static int[][] map;
    static int[][][] isVisited;

    static int[] dr = new int[]{0,0,1,-1};
    static int[] dc = new int[]{1,-1,0,0};
    static StringBuilder sb = new StringBuilder();
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;


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

        process();
    }

    private static void process() {

        System.out.println(bfs());
    }

    private static int bfs() {
        Queue&amp;lt;Position&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        isVisited[0][0][0] = 1;
        q.add(new Position(0,0,0));

        while(!q.isEmpty()){
            Position position = q.poll();

            if(position.r == N-1 &amp;amp;&amp;amp; position.c == M-1) return isVisited[position.breakingFlag][position.r][position.c];

            for (int i = 0; i &amp;lt; 4; i++) {
                int mr = position.r + dr[i];
                int mc = position.c + dc[i];
                int nextBreakingFlag = position.breakingFlag;

                if(mr &amp;lt; 0 || mc &amp;lt; 0 || mr&amp;gt;= N || mc &amp;gt;= M)   continue;
                if(isVisited[position.breakingFlag][mr][mc] &amp;gt; 0) continue;

                if(map[mr][mc] == 0){
                    isVisited[nextBreakingFlag][mr][mc] = isVisited[position.breakingFlag][position.r][position.c] + 1;
                    q.add(new Position(mr,mc, position.breakingFlag));
                }
                if(map[mr][mc] == 1 &amp;amp;&amp;amp; nextBreakingFlag == 0){
                    isVisited[1][mr][mc] = isVisited[position.breakingFlag][position.r][position.c] + 1;
                    nextBreakingFlag = 1;
                    q.add(new Position(mr,mc, nextBreakingFlag));
                }
            }
        }

        return -1;

    }

    private static void init() throws IOException {
        st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];
        isVisited = new int[2][N][M];

        for (int i = 0; i &amp;lt; N; i++) {
            char[] inputs = br.readLine().toCharArray();

            for (int j = 0; j &amp;lt; M; j++) {
                map[i][j] = inputs[j] - '0';
            }
        }
    }

    static class Position{
        int r;
        int c;
        int breakingFlag;

        Position(int r, int c, int breakingFlag){
            this.r = r;
            this.c = c;
            this.breakingFlag = breakingFlag;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/BFS, DFS</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/76</guid>
      <comments>https://delpho.tistory.com/76#entry76comment</comments>
      <pubDate>Tue, 9 Apr 2024 17:30:32 +0900</pubDate>
    </item>
    <item>
      <title>JSP MVC Pattern (등장배경, Model 1 / Model 2, 한계)</title>
      <link>https://delpho.tistory.com/75</link>
      <description>&lt;h1&gt;1. MVC Pattern 등장 배경&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MVC 패턴은 JSP의 등장 이후 제안된 패턴이 아니라, 1970년대 후반에 제안된 패턴입니다. Smalltalk 언어를 고도화하는데에 사용되었다고 알고 있습니다. (이전 포스팅 참고)&lt;/li&gt;
&lt;li&gt;프레젠테이션 로직과 비즈니스 로직이 분리되어있지 않은 Servlet의 단점을 해결하기 위해 JSP가 등장하였고, 이 JSP를 더욱 효과적으로 사용하기 위해 MVC 패턴을 적용하기 시작했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ Servlet과 JSP ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Servlet 코드&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@WebServlet(name = &quot;memberSaveServlet&quot;, urlPatterns = &quot;/servlet/members/save&quot;)
public class MemberSaveServlet extends HttpServlet {

    private final MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        final String username = request.getParameter(&quot;username&quot;);
        final int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
        final Member member = new Member(username, age);
        memberRepository.save(member);

        response.setContentType(&quot;text/html&quot;);
        response.setCharacterEncoding(&quot;utf-8&quot;);
        final PrintWriter printWriter = response.getWriter();
        printWriter.write(&quot;&amp;lt;html&amp;gt;\\n&quot; +
            &quot;&amp;lt;head&amp;gt;\\n&quot; +
            &quot; &amp;lt;meta charset=\\&quot;UTF-8\\&quot;&amp;gt;\\n&quot; +
            &quot;&amp;lt;/head&amp;gt;\\n&quot; +
            &quot;&amp;lt;body&amp;gt;\\n&quot; +
            &quot; &amp;lt;li&amp;gt;id=&quot; + member.getId() + &quot;&amp;lt;/li&amp;gt;\\n&quot; +
            &quot; &amp;lt;li&amp;gt;username=&quot; + member.getUsername() + &quot;&amp;lt;/li&amp;gt;\\n&quot; +
            &quot; &amp;lt;li&amp;gt;age=&quot; + member.getAge() + &quot;&amp;lt;/li&amp;gt;\\n&quot; +
            &quot;&amp;lt;/body&amp;gt;\\n&quot; +
            &quot;&amp;lt;/html&amp;gt;&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP 코드&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;%@ page import=&quot;hello.servlet.basic.domain.member.Member&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;hello.servlet.basic.domain.member.MemberRepository&quot; %&amp;gt;
&amp;lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&amp;gt;
&amp;lt;%
    final MemberRepository memberRepository = MemberRepository.getInstance();
    final String username = request.getParameter(&quot;username&quot;);
    final int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
    final Member member = new Member(username, age);
    memberRepository.save(member);
%&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
성공
&amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;id=&amp;lt;%=member.getId()%&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;username=&amp;lt;%=member.getUsername()%&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;age=&amp;lt;%=member.getAge()%&amp;gt;
    &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;a href=&quot;/index.html&quot;&amp;gt;메인&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&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;서블릿과 JSP 모두, 뷰와 비즈니스 로직이 결합되어있다.&lt;br /&gt;&lt;br /&gt;&amp;rArr; 변경 생명 주기가 다른 두 가지 요소가 결합되어있다!!&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;비즈니스 로직은 Servlet이, 프레젠테이션 로직은 JSP가 담당하도록 하기 위해 MVC 패턴 적용(Model, View, Controller)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;2. MVC Pattern&lt;/h1&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 애플리케이션을 Model, View, Controller 세 가지 구성요소로 구분한 패턴&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;[ Model ]&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터와 비즈니스 로직, 그리고 데이터의 처리 방법을 관리하는 핵심 구성 요소&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: 데이터의 저장, 조회, 수정, 삭제와 같은 데이터 관리와 비즈니스 로직의 실행을 담당합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 모델은 다른 구성 요소(View, Controller)와 독립적으로 작동하여, 데이터 변경이 있을 경우 이를 View에 반영할 수 있도록 설계됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ View ]&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자에게 정보를 표시하는 부분, HTML, JSP 등을 사용하여 구현&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: 모델에서 처리한 데이터를 사용자에게 시각적으로 표현합니다. 사용자의 요청에 따라 다른 데이터를 표시하는 역할도 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: View는 Model에 직접적으로 데이터를 요청하지 않고, Controller를 통해 필요한 데이터를 전달받아 사용자에게 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ Controller ]&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 입력을 받아 비즈니스 로직을 처리하는 부분입니다. 사용자의 요청을 분석하여 Model을 조작하고, 그 결과를 View에 전달합니다.&lt;/p&gt;
&lt;/blockquote&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;: 사용자의 요청을 받아들여, 해당 요청에 맞는 Model의 데이터 처리를 요청하고, 처리 결과를 바탕으로 적절한 View를 선택하여 사용자에게 응답합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: Controller는 Model과 View 사이의 중재자 역할을 하며, Model과 View의 직접적인 의존성을 제거함으로써 애플리케이션의 유연성과 확장성을 증가시킵니다.&lt;/li&gt;
&lt;/ul&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;[ JSP MVC 패턴의 흐름 ]&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자 요청&lt;/b&gt;: 사용자는 웹 브라우저를 통해 특정 작업을 요청합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Controller 동작&lt;/b&gt;: 요청을 받은 Controller는 요청의 성격을 분석합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model 조작&lt;/b&gt;: Controller는 분석 결과에 따라 적절한 Model에 데이터 처리를 요청합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 반환&lt;/b&gt;: Model은 요청받은 데이터 처리 작업을 수행한 후 결과를 Controller에 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;View 선택&lt;/b&gt;: Controller는 Model로부터 반환받은 데이터를 바탕으로 적절한 View를 선택합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;응답 생성&lt;/b&gt;: 선택된 View는 Controller로부터 받은 데이터를 사용하여 사용자에게 보여줄 응답을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자에게 응답 전송&lt;/b&gt;: 생성된 응답은 사용자에게 전송되어 사용자의 웹 브라우저에 표시됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;3. Model 1 / Model 2 Architecture&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ Model 1 Architecture ]&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP 페이지가 직접 요청을 처리하고, 비즈니스 로직을 실행하며, 응답을 생성하는 구조입니다. (구성 요소에 Servlet이 따로 없음)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 아키텍처에서는 JSP 페이지 내에서 JavaBeans 컴포넌트를 사용하여 데이터를 처리하고, 비즈니스 로직을 구현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 구현이 간단하고 직관적이며, 작은 프로젝트나 단순한 웹 애플리케이션에 적합합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: JSP 페이지 내에 프레젠테이션 로직과 비즈니스 로직이 혼재되어 있어 유지보수와 확장이 어렵습니다. 이로 인해 애플리케이션이 복잡해질수록 관리가 힘들어지며, 재사용성이 낮아집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cnz0M/btsGq1SLnYq/qqUXWBpcXs4S1Hs1fqAr6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cnz0M/btsGq1SLnYq/qqUXWBpcXs4S1Hs1fqAr6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cnz0M/btsGq1SLnYq/qqUXWBpcXs4S1Hs1fqAr6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCnz0M%2FbtsGq1SLnYq%2FqqUXWBpcXs4S1Hs1fqAr6k%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;899&quot; height=&quot;393&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;393&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;JSP 예시&lt;/h3&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;%@ page import=&quot;java.util.*, myapp.models.UserDAO&quot; %&amp;gt;
&amp;lt;%
    UserDAO userDAO = new UserDAO();
    List users = userDAO.getAllUsers();
    request.setAttribute(&quot;users&quot;, users);
%&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h2&amp;gt;User List&amp;lt;/h2&amp;gt;
    &amp;lt;ul&amp;gt;
        &amp;lt;% for(int i=0; i&amp;lt;users.size(); i++) { %&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;%= users.get(i).getName() %&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;% } %&amp;gt;
    &amp;lt;/ul&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;[ Model 2 Architecture = 일반적으로 말하는 MVC 패턴 ]&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 패턴을 기반으로 한 구조로, 애플리케이션을 모델(Model), 뷰(View), 컨트롤러(Controller)의 세 부분으로 나눕니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 아키텍처에서는 컨트롤러 역할을 하는 서블릿이 사용자 요청을 받아 처리하고, 적절한 비즈니스 로직을 모델을 통해 실행한 후 결과를 뷰에 전달하여 사용자에게 응답을 보여줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 프레젠테이션 로직과 비즈니스 로직이 분리되어 있어 유지보수와 확장이 용이합니다. 애플리케이션의 규모가 커질수록 이 아키텍처의 장점이 더욱 부각됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: 모델 1 아키텍처에 비해 초기 구축이 복잡할 수 있으며, 작은 프로젝트에서는 과도한 설계로 인식될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKBuEd/btsGuJW5rAP/jsmyJALgxRKUvKSGjiEBlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKBuEd/btsGuJW5rAP/jsmyJALgxRKUvKSGjiEBlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKBuEd/btsGuJW5rAP/jsmyJALgxRKUvKSGjiEBlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKBuEd%2FbtsGuJW5rAP%2FjsmyJALgxRKUvKSGjiEBlk%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;906&quot; height=&quot;384&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;384&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Servlet 예시 2개&lt;/h3&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import myapp.models.UserDAO;

public class UserListServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        UserDAO userDAO = new UserDAO();
        List users = userDAO.getAllUsers();
        request.setAttribute(&quot;users&quot;, users);
        RequestDispatcher dispatcher = request.getRequestDispatcher(&quot;/userList.jsp&quot;);
        dispatcher.forward(request, response);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;@WebServlet(name = &quot;mvcMemberListServlet&quot;,urlPatterns = &quot;/servlet-mvc/members&quot;)
public class MvcMemberListServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List&amp;lt;Member&amp;gt; members = memberRepository.findAll();
        request.setAttribute(&quot;members&quot;, members);
        String viewPath = &quot;/WEB-INF/views/members.jsp&quot;;
        RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
        requestDispatcher.forward(request, response);
    }
}
&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;JSP 예시 2개&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&amp;gt;
&amp;lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;a href=&quot;/index.html&quot;&amp;gt;메인&amp;lt;/a&amp;gt;
&amp;lt;table&amp;gt;
    &amp;lt;thead&amp;gt;
    &amp;lt;th&amp;gt;id&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;username&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;age&amp;lt;/th&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
    &amp;lt;c:forEach var=&quot;item&quot; items=&quot;${members}&quot;&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;${item.id}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;${item.username}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;${item.age}&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/c:forEach&amp;gt;
    &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;5. JSP MVC 패턴의 한계&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 복잡한 구성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;구성의 복잡성&lt;/b&gt;: 모델 2 아키텍처는 분리된 컨트롤러, 모델, 뷰의 구성으로 인해 초기 설정이 복잡하고, 프로젝트의 구조를 이해하고 관리하기가 더 어려울 수 있습니다. 특히 큰 프로젝트에서는 다수의 컨트롤러와 뷰가 상호작용하면서 이해와 관리의 복잡도가 증가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 뷰와 로직의 혼재&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;JSP 내의 로직 혼재&lt;/b&gt;: JSP 페이지 내에서 Java 코드를 사용하여 비즈니스 로직이나 흐름 제어를 처리하는 것은 권장되지 않습니다. 이는 뷰와 로직의 명확한 분리를 어렵게 만들며, 코드의 유지보수와 가독성을 저하시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 재사용성과 유연성의 제한&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴포넌트 재사용의 어려움&lt;/b&gt;: JSP와 서블릿만을 사용하여 개발할 때, UI 컴포넌트의 재사용성이 낮고, 화면 간의 데이터 전달 및 재사용 로직을 구현하기 위한 추가적인 노력이 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프론트엔드와 백엔드의 경계&lt;/b&gt;: 최근 웹 개발 트렌드는 프론트엔드와 백엔드를 명확히 분리하는 방향으로 이동하고 있습니다. JSP 기반의 애플리케이션은 이러한 구조적 분리를 어렵게 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;4. 반복적인 코드 작성&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공통된 요청 처리 로직(예: 권한 검사, 로깅, 요청 데이터 파싱 등)을 각 컨트롤러나 JSP 페이지마다 반복적으로 작성해야 합니다. 이는 개발 과정에서 중복 코드가 많아지고, 유지보수의 어려움을 증가시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;5. 요청 처리의 일관성 부족&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디스패처 서블릿 같은 중앙 집중식 요청 처리 메커니즘이 없을 경우, 각 요청을 처리하는 방식의 일관성을 유지하기 어려워집니다. 개발자가 각각의 컨트롤러나 JSP 페이지에서 요청 처리 로직을 개별적으로 구현해야 하기 때문에, 애플리케이션 전반에 걸친 처리 로직의 표준화가 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;6. 라우팅 및 컨트롤러 관리의 복잡성&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 요청 URL을 처리할 컨트롤러를 직접 매핑해야 하며, 복잡한 웹 애플리케이션에서는 이러한 매핑과 관리가 복잡하고 어려워질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 4,5,6번의 한계를 주목해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP MVC Pattern을 보면, 클라이언트에서 들어오는 호출을 할때마다 각각의 Controller (서블릿)들이 구동됩니다. 하지만, 서블릿이 가지는 공통된 부분을 매번 작성해야되기에 비효율적입니다. 공통된 부분이 있으면&amp;nbsp;&lt;b&gt;이를 한번에 처리해주는 일종의 수문장 역할&lt;/b&gt;을 하는 무언가 있으면 더 좋을 것 같습니다. 위한 같은 한계점을 돌파하기 위해 스프링 MVC는&amp;nbsp;&lt;b&gt;Dispatcher Servlet&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sTFcT/btsGuL1ANO5/cjLqEAxF1LtV5mdmkyj2uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sTFcT/btsGuL1ANO5/cjLqEAxF1LtV5mdmkyj2uk/img.png&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;646&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.3645%; margin-right: 10px;&quot; data-widthpercent=&quot;45.9&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sTFcT/btsGuL1ANO5/cjLqEAxF1LtV5mdmkyj2uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsTFcT%2FbtsGuL1ANO5%2FcjLqEAxF1LtV5mdmkyj2uk%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;948&quot; height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4OmOt/btsGrfXtbt1/Mi7pVMDcoGGYtAdSXwb9Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4OmOt/btsGrfXtbt1/Mi7pVMDcoGGYtAdSXwb9Q1/img.png&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;470&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.4728%;&quot; data-widthpercent=&quot;54.1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4OmOt/btsGrfXtbt1/Mi7pVMDcoGGYtAdSXwb9Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4OmOt%2FbtsGrfXtbt1%2FMi7pVMDcoGGYtAdSXwb9Q1%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;813&quot; height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/div&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;style3&quot;&gt;Model과 View는 서로의 정보를 갖고 있지 않는 독립적인 상태라고 하지만 Model과 View사이에는 Controller를 통해 소통을 이루기에 의존성이 완전히 분리될 수 없습니다. 그래서 복잡한 대규모 프로그램의 경우 다수의 View와 Model이 Controller를 통해 연결되기 때문에 컨트롤러가 불필요하게 커지는 현상이 발생하기도 합니다. 이러한 현상을 Massive-View-Controller 현상이라고 하며 이를 보완하기 위해 MVP, MVVM, Flux, Redux등의 다양한 패턴들이 생겨났습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;6. JSP MVC pattern의 한계뿐만 아니라..&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. EJB(Enterprise JavaBeans)의 복잡성과 성능 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring이 처음 등장했을 때, 많은 엔터프라이즈 자바 애플리케이션은 EJB를 사용하고 있었습니다. EJB는 분산 컴포넌트와 트랜잭션 관리를 제공하는 강력한 기술이었지만, 설정과 사용이 복잡하고, 특히 성능 문제가 종종 발생했습니다. Spring은 이러한 EJB의 복잡성을 대체하고, 더 간단하며 가벼운 방식으로 엔터프라이즈 애플리케이션을 개발할 수 있는 방법을 제공하고자 했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 보일러플레이트 코드의 감소 필요성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존의 Java EE 개발에서는 반복되는 코드 패턴이 많이 요구되었습니다. 예를 들어, JDBC를 사용한 데이터베이스 작업, 초기 J2EE 애플리케이션에서의 리소스 관리 등은 많은 반복 코드를 필요로 했습니다. Spring은 의존성 주입(DI), 템플릿 메소드 디자인 패턴 등을 통해 이러한 반복되는 코드를 줄이는 방법을 제공함으로써 개발자의 생산성을 향상시키고자 했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 의존성 관리와 결합도 감소&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대규모 애플리케이션에서는 클래스 간의 의존성 관리가 중요한 이슈가 됩니다. 기존 방식에서는 컴포넌트 간의 높은 결합도로 인해 변경이 어렵고 테스트가 어려운 문제가 있었습니다. Spring의 의존성 주입 기능은 이러한 문제를 해결하고, 느슨한 결합을 통해 유연성을 높이고자 했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등등으로 인해 다음 포스팅에 나올 Spring Framework가 등장하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Spring</category>
      <author>delpho</author>
      <guid isPermaLink="true">https://delpho.tistory.com/75</guid>
      <comments>https://delpho.tistory.com/75#entry75comment</comments>
      <pubDate>Mon, 8 Apr 2024 22:37:09 +0900</pubDate>
    </item>
  </channel>
</rss>