<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>뚜비의 개발로그</title>
    <link>https://eenzhd.tistory.com/</link>
    <description>1년차 백엔드&amp;amp;iOS 개발자의 감자 탈출 블로그  </description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 17:06:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor> 뚜비</managingEditor>
    <image>
      <title>뚜비의 개발로그</title>
      <url>https://tistory1.daumcdn.net/tistory/7282703/attach/2af5f7541fee4ec589987ca67cb84334</url>
      <link>https://eenzhd.tistory.com</link>
    </image>
    <item>
      <title>[프로그래머스]  모음 사전 - JAVA</title>
      <link>https://eenzhd.tistory.com/253</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciksvS/btsQpE5U9Uu/msaurEpiUUeJ823DMlJv71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciksvS/btsQpE5U9Uu/msaurEpiUUeJ823DMlJv71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciksvS/btsQpE5U9Uu/msaurEpiUUeJ823DMlJv71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciksvS%2FbtsQpE5U9Uu%2FmsaurEpiUUeJ823DMlJv71%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;675&quot; height=&quot;720&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1757252032439&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.ArrayList;
import java.util.List;

class Solution {

    static char[] vowels = {'A', 'E', 'I', 'O', 'U'};
    static List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;();

    public int solution(String word) {
        makeWords(&quot;&quot;, 0);
        return list.indexOf(word) + 1;
    }

    private void makeWords(String str, int depth) {
        if(depth &amp;gt; 5) {
            return; // 5글자 이상이면 리턴
        }
        if(depth &amp;gt; 0) { // 1글자 이상이면 리스트에 추가
            list.add(str);
        }

        for (char c : vowels) {
            makeWords(str + c, depth + 1);
        }

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A, E, I, O, U를 이용해 길이 1부터 5까지 만들 수 있는 모든 조합을 만들어서 리스트에 저장하고 word의 위치를 찾는 방식으로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list.indexOf(word)를 이용하면 인덱스를 구할 수 있고, 순서는 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;makeWords(&quot;&quot;, 0) 에서 시작하게 되면 &quot;&quot;은 아직 빈 문자열이고 depth는 0이므로 리스트에 추가되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;depth가 5를 초과할 경우에는 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 &quot;A&quot; 생성 후 리스트에 저장하고 메서드를 재귀호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;AA&quot;, &quot;AAA&quot;, &quot;AAAA&quot;, &quot;AAAAA&quot;... 순서로 만들어지고 저장된다.&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>Java</category>
      <category>모음사전</category>
      <category>프로그래머스</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/253</guid>
      <comments>https://eenzhd.tistory.com/253#entry253comment</comments>
      <pubDate>Sun, 7 Sep 2025 22:39:06 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] Java로 배우는 누적합/구간합 - feat.백준(구간합 구하기4)</title>
      <link>https://eenzhd.tistory.com/252</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;특정 구간의 합을 여러 번 구해야 하는 문제를 풀 때, 단순하게 for문을 사용해서 매번 합을 구하면 시간 초과가 발생할 수 있다. 이런 경우에는 누적합(Prefix Sum)을 활용해서 빠르게 구간합을 구하는 방법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-10 오후 7.02.52.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nA51n/btsMF5mF8tH/kZeM7PijMweZvRR6RMVHR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nA51n/btsMF5mF8tH/kZeM7PijMweZvRR6RMVHR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nA51n/btsMF5mF8tH/kZeM7PijMweZvRR6RMVHR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnA51n%2FbtsMF5mF8tH%2FkZeM7PijMweZvRR6RMVHR0%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;651&quot; height=&quot;305&quot; data-filename=&quot;스크린샷 2025-03-10 오후 7.02.52.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;누적합&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누적합은 배열의 각 위치까지의 합을 미리 구해놓은 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누적합 배열을 사용하면 어떤 구간의 합도 O(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;예를 들어 배열이 [5, 4, 3, 2, 1]일 때, 누적합 배열을 만들면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(* 편한 계산을 위해 인덱스 0번은 비워두고 배열의 크기는 N+1로 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;인덱스&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;원본 배열&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;누적합 배열&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;12&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%;&quot;&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;p data-ke-size=&quot;size16&quot;&gt;누적합 배열을 이용하면 i번째 수부터 j번째 수까지의 합은 다음 공식으로 구할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;u&gt;구간합(i, j)&lt;/u&gt; = S[j] - S[i-1]&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;S[j] : 1번부터 j번까지의 합&lt;/li&gt;
&lt;li&gt;S[i-1] : 1번부터 (i-1)번까지의 합&lt;/li&gt;
&lt;li&gt;따라서, 두 값을 빼주면 i부터 j까지의 합이 나온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  예를 들어 2~4 구간의 합을 구한다면,&lt;br /&gt;S[4] - S[1] = 14 - 5= 9&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;h2 data-ke-size=&quot;size26&quot;&gt;풀이&lt;/h2&gt;
&lt;pre id=&quot;code_1741601051686&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();

        int[] prefixSum = new int[n + 1];
        for (int i = 1; i &amp;lt; prefixSum.length; i++) {
            int num = sc.nextInt();
            prefixSum[i] = num + prefixSum[i - 1];
        }

        for (int i = 0; i &amp;lt; m; i++) {
            int start = sc.nextInt();
            int end = sc.nextInt();
            int sum = prefixSum[end] - prefixSum[start - 1];
            System.out.println(sum);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>Java</category>
      <category>구간합</category>
      <category>구간합 구하기4</category>
      <category>누적합</category>
      <category>백준</category>
      <category>백준 11659번</category>
      <category>알고리즘</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/252</guid>
      <comments>https://eenzhd.tistory.com/252#entry252comment</comments>
      <pubDate>Mon, 10 Mar 2025 19:07:20 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] Java로 배우는 Dynamic Programming(동적 계획법) - feat.백준(계단 오르기)</title>
      <link>https://eenzhd.tistory.com/251</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dynamic Programming(동적 계획법)&lt;/h2&gt;
&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;DP 구현 방식은 4가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모이제이션(memoization) 방법: 모든 작은 문제들을 한 번만 계산해서 DP에 저장하며, 저장한 값을 재사용 하는 방법&lt;/li&gt;
&lt;li&gt;타뷸레이션(Tabulation) 방법: 작은 문제부터 순차적으로 계산하며 상위로 올라가는 방법&lt;/li&gt;
&lt;li&gt;탑 다운: 큰 문제에서 작은 하위 문제들을 확인해가며 진행하는 방법&lt;/li&gt;
&lt;li&gt;바텀 업: = 타뷸레이션&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 계획법은 분할 정복과 비슷해보이지만 차이점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분할 정복의 경우 분할된 하위 문제가 동일하게 중복이 일어나지 않는 경우에 사용되고,&lt;/li&gt;
&lt;li&gt;동적 계획법은 분할된 하위 문제가 중복이 일어나는 경우에 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 백준 문제는 계단을 밟아 올라가는 과정에서 가능한 최댓값을 구하기 위해 이전에 계산 된 결과를 활용(&lt;u&gt;메모이제이션 방법&lt;/u&gt;)하여 풀 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-15 오후 7.25.32.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;1218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/635hn/btsLOCeRKad/KkE49eIgmAaMKzX2Y4hdTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/635hn/btsLOCeRKad/KkE49eIgmAaMKzX2Y4hdTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/635hn/btsLOCeRKad/KkE49eIgmAaMKzX2Y4hdTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F635hn%2FbtsLOCeRKad%2FKkE49eIgmAaMKzX2Y4hdTK%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;1774&quot; height=&quot;1218&quot; data-filename=&quot;스크린샷 2025-01-15 오후 7.25.32.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;1218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-15 오후 7.25.56.png&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFXWlR/btsLO7rQMhl/prUUh4RZ8oahTkNiho38N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFXWlR/btsLO7rQMhl/prUUh4RZ8oahTkNiho38N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFXWlR/btsLO7rQMhl/prUUh4RZ8oahTkNiho38N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFXWlR%2FbtsLO7rQMhl%2FprUUh4RZ8oahTkNiho38N0%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;1776&quot; height=&quot;826&quot; data-filename=&quot;스크린샷 2025-01-15 오후 7.25.56.png&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736937496090&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        int[] score = new int[num + 1];
        int[] dp = new int[num + 1];

        for (int i = 1; i &amp;lt;= num; i++) {
            score[i] = sc.nextInt();
        }

        dp[1] = score[1];

        if (num &amp;gt; 1) {
            dp[2] = score[1] + score[2];
        }
        for (int i = 3; i &amp;lt;= num; i++) {
            dp[i] = Math.max(dp[i - 2], dp[i - 3] + score[i - 1]) + score[i];
        }
        System.out.println(dp[num]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736937553460&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] dp = new int[num + 1];  // 최댓값을 저장할 dp 배열 (1번부터 num번까지 사용)&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;dp[] 배열은 각 계단까지 올라갈 때 얻을 수 있는 최대값을 저장할 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp[i]는 i번째 계단에 도달했을 때의 최댓값을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736937615339&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dp[1] = score[1];  // 첫 번째 계단은 반드시 밟으므로 dp[1] = score[1]
if (num &amp;gt; 1) {
    dp[2] = score[1] + score[2];  // 두 번째 계단까지 올라가는 최댓값은 첫 번째 계단과 두 번째 계단을 순서대로 밟는 것
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 계단은 반드시 밟아야 하므로 dp[1]은 score[1]의 값으로 초기화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 계단까지 올라가는 경우, 첫 번째 계단과 두 번째 계단을 차례로 밟을 수 있으므로 dp[2] = score[1] + score[2]로 초기화한다. 이 때, 계단의 수가 1일 경우 두 번째 계단은 존재하지 않기 때문에 num &amp;gt; 1 조건문을 통해 예외처리를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736937857510&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int i = 3; i &amp;lt;= num; i++) {
    dp[i] = Math.max(dp[i - 2], dp[i - 3] + score[i - 1]) + score[i];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 계단부터 마지막 계단까지 올라가는 최댓값은 두 가지 방법을 고려해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;i-2번째 계단을 밟고 한 계단 건너 뛰기 : 이 경우 dp[i-2]에 score[i]를 더한 값이 된다.&lt;/li&gt;
&lt;li&gt;i-3번째 계단을 밟고, i-1번째 계단과 i번째 계단을 차례로 밟기 : 이 경우는 dp[i-3] + score[i-1] + score[i]가 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번과 2번 둘 중 더 큰 값을 저장하여 dp[i]에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 계단에 도달했을 때 얻을 수 있는 최댓값은 dp[num]에 저장되어 있으므로 dp[num] 값을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>dp 알고리즘</category>
      <category>Dynamic Programming</category>
      <category>Java</category>
      <category>동적 계획법</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/251</guid>
      <comments>https://eenzhd.tistory.com/251#entry251comment</comments>
      <pubDate>Wed, 15 Jan 2025 19:50:50 +0900</pubDate>
    </item>
    <item>
      <title>[스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] MVC 패턴</title>
      <link>https://eenzhd.tistory.com/250</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;MVC 패턴 - 개요&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서블릿과 JSP의 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿으로 개발할 때는 뷰(View) 화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여 지저분하고 복잡해진다. JSP를 사용하면 뷰를 생성하는 HTML 작업을 깔끔하게 가져가고, 동적으로 변경이 필요한 부분에만 자바코드를 적용할 수 있다.&lt;/p&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 코드 상위 부분의 절반은 비즈니스 로직이고, 나머지 하위 절반은 결과를 HTML로 보여주기 위한 뷰 영역이 된다. JSP에는 Java 코드, Repository 등 다양한 코드가 모두 노출되며 너무 많은 역할을 가져가게 된다.&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로 비즈니스 로직과 뷰 렌더링을 모두 처리하게 되면 유지보수가 어려워진다. 제일 큰 문제점은 둘 사이에 변경의 라이프 사이클이 다르다는 점이다. 예를 들면 UI를 일부 수정하는 일과 비즈니스 로직을 수정하는 일은 각각 다르게 발생할 가능성이 매우 높고 대부분 서로에게 영향을 주지 않는다. 이렇듯 변경 라이프 사이클이 다른 부분을 하나의 코드로 관리하는 것은 유지보수에 좋지 않다.&lt;/p&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 View Controller&lt;/h2&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;: HTTP 요청을 받아 파라미터를 검증하고 비즈니스 로직을 실행한다. 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모델&lt;/b&gt;: 뷰에 츌력할 데이터를 남는다. 뷰가 필요한 데이터를 모델에 담아 전달해주기 때문에 비즈니스 로직이나 데이터 접근을 몰라도 되고 화면을 렌더링 하는 일에 집중할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뷰&lt;/b&gt;: 모델에 담겨있는 데이터를 사용해서 화면에 그린다.(HTML 생성)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-18 오후 10.40.55.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IVaUB/btsLmOt3eBq/0tHOucdzbwa71mNWeLf2V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IVaUB/btsLmOt3eBq/0tHOucdzbwa71mNWeLf2V0/img.png&quot; data-alt=&quot;출처: 김영한님&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IVaUB/btsLmOt3eBq/0tHOucdzbwa71mNWeLf2V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIVaUB%2FbtsLmOt3eBq%2F0tHOucdzbwa71mNWeLf2V0%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;488&quot; height=&quot;268&quot; data-filename=&quot;스크린샷 2024-12-18 오후 10.40.55.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: 김영한님&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러에 비즈니스 로직을 두게 되면 컨트롤러가 너무 많은 역할을 하게 되므로 비즈니스 로직은 서비스(Service) 계층에서 별도로 처리한다. 그리고 컨트롤러는 비즈니스 로직이 있는 서비스를 호출하는 역할을 담당한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;MVC 패턴 - 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 컨트롤러로 사용하고 JSP를 뷰로 사용해서 MVC 패턴을 적용해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model은 HttpServletRequest 객체를 사용한다. request는 내부에 데이터 저장소를 가지고 있는데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;request.setAttribute(), request.getAttribute()를 사용하면 데이터를 보관하고 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734531798275&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package hello.servlet.web.servletmvc;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name=&quot;mvcMemberFormServlet&quot;, urlPatterns = &quot;/servlet-mvc/members/new-form&quot;)
public class MvcMemberFormServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String viewPath = &quot;/WEB-INF/views/new-form.jsp&quot;;
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);  // 다른 서블릿이나 JSP로 이동. 서버 내부에서 다시 호출 발생.
    }
}&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;dispatcher.forward()는 다른 서블릿이나 JSP로 이동할 수 있는 기능이며 서버 내부에서 다시 호출이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/WEB-INF 경로 안에 JSP가 있으면 외부에서 직접 JSP를 호출할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redirect는 실제 클라이언트(웹 브라우저)에 응답이 나갔다가 클라이언트가 redirect 경로로 다시 요청하므로 클라이언트가 인지할 수 있고 URL 경로도 실제로 변경된다. forward는 서버 내부에서 일어나는 호출이기 때문에 클라이언트가 전혀 인지하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-18 오후 11.25.13.png&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EontC/btsLmMJGU5Z/IG6x6aFCFqc3rr3t9BoEVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EontC/btsLmMJGU5Z/IG6x6aFCFqc3rr3t9BoEVk/img.png&quot; data-alt=&quot;/servlet-mvc/members/new-form&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EontC/btsLmMJGU5Z/IG6x6aFCFqc3rr3t9BoEVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEontC%2FbtsLmMJGU5Z%2FIG6x6aFCFqc3rr3t9BoEVk%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;484&quot; height=&quot;54&quot; data-filename=&quot;스크린샷 2024-12-18 오후 11.25.13.png&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;/servlet-mvc/members/new-form&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734531952085&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package hello.servlet.web.servletmvc;

import hello.servlet.basic.domain.member.Member;
import hello.servlet.basic.domain.member.MemberRepository;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(name=&quot;/mvcMemberSaveServlet&quot;, urlPatterns = &quot;/servlet-mvc/members/save&quot;)
public class MvcMemberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter(&quot;username&quot;);
        int age = Integer.valueOf(request.getParameter(&quot;age&quot;));

        Member member = new Member(username, age);
        memberRepository.save(member);

        // Model에 data를 보관한다.
        request.setAttribute(&quot;member&quot;, member);

        String viewPath = &quot;/WEB-INF/views/save-result.jsp&quot;;
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);

        dispatcher.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;HttpServletRequest를 Model로 사용한다. request가 제공하는 setAttribute()를 사용하면 request 객체에 데이터를 보관해서 뷰에 전달할 수 있다. 뷰는 request.getAttribute()를 사용해서 데이터를 꺼낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC를 사용해서 컨트롤러 로직과 뷰 로직을 분리할 수 있으며 화면 수정이 필요할 경우에는 뷰 로직만 변경하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-18 오후 11.26.50.png&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqAGkm/btsLo8YlkX1/X7GOLGyAMTuURunPoK0irk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqAGkm/btsLo8YlkX1/X7GOLGyAMTuURunPoK0irk/img.png&quot; data-alt=&quot;/servlet-mvc/members/save&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqAGkm/btsLo8YlkX1/X7GOLGyAMTuURunPoK0irk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqAGkm%2FbtsLo8YlkX1%2FX7GOLGyAMTuURunPoK0irk%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;199&quot; height=&quot;158&quot; data-filename=&quot;스크린샷 2024-12-18 오후 11.26.50.png&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;/servlet-mvc/members/save&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734532073175&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package hello.servlet.web.servletmvc;

import hello.servlet.basic.domain.member.Member;
import hello.servlet.basic.domain.member.MemberRepository;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

@WebServlet(name=&quot;mvcMemberListServlet&quot;, urlPatterns = &quot;/servlet-mvc/members&quot;)
public class MvcMemberListServlet extends HttpServlet {

    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 dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.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;request 객체를 사용해서 List&amp;lt;Member&amp;gt; members를 모델에 보관한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-18 오후 11.28.33.png&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GjetD/btsLn85NZpy/KQnEv3uekdDjPX6EK6K9sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GjetD/btsLn85NZpy/KQnEv3uekdDjPX6EK6K9sK/img.png&quot; data-alt=&quot;/servlet-mvc/members&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GjetD/btsLn85NZpy/KQnEv3uekdDjPX6EK6K9sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGjetD%2FbtsLn85NZpy%2FKQnEv3uekdDjPX6EK6K9sK%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;192&quot; height=&quot;144&quot; data-filename=&quot;스크린샷 2024-12-18 오후 11.28.33.png&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;/servlet-mvc/members&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;MVC 패턴 - 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 패턴은 컨트롤러와 뷰의 역할을 명확하게 구분할 수 있게 해준다. 뷰는 화면을 그리는 역할에 충실해지기 때문에 코드가 깔끔하고 직관적이다. 단순하게 모델에서 필요한 데이터를 꺼내 화면을 만들 수 있다. 하지만 컨트롤러는 중복이 너무 많고 필요하지 않은 코드들도 아직 많아 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. forward 중복&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View로 이동하는 코드가 항상 중복 호출된다. 메서드를 공통화해도 되지만 해당 메서드도 항상 직접 호출해야하는 단점이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1734532264279&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. ViewPath 중복&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP가 아닌 thymeleaf와 같은 다른 뷰로 변경하게 된다면 전체 코드를 변경해야 하는 단점이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1734532330397&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; String viewPath = &quot;/WEB-INF/views/...&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 사용하지 않는 코드&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HttpServletRequest, HttpServletResponse를 사용할 때도 있고 사용하지 않을 때도 있다. 해당 코드를 사용하면 테스트 케이스를 작성하기 어려워지기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;4. 공통 처리 어려움&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능이 복잡해질 수록 컨트롤러에서 공통으로 처리해야 하는 부분이 증가하게 되는데, 메서드를 만들어도 항상 호출해야 하고 호출하는 자체도 중복이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 컨트롤러 호출 전에 공통 기능을 처리해야 한다. &lt;u&gt;프론트 컨트롤러(Front Controller) 패턴&lt;/u&gt;을 사용하면 문제를 해결할 수 있으며 스프링 MVC 핵심도 프론트 컨트롤러에 있다.&lt;/p&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;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;인프런 김영한님 강의&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;인프런의 김영한님 스프링 강의&lt;/b&gt;&lt;/u&gt;를 보고 작성한 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;강의를 들으면서 정리한 글이므로 틀린 내용이나 오타가 있을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-End/Spring</category>
      <category>controller</category>
      <category>Java</category>
      <category>jsp</category>
      <category>Model</category>
      <category>MVC</category>
      <category>MVC패턴</category>
      <category>spring</category>
      <category>View</category>
      <category>김영한</category>
      <category>스프링</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/250</guid>
      <comments>https://eenzhd.tistory.com/250#entry250comment</comments>
      <pubDate>Wed, 18 Dec 2024 23:35:18 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] Java로 배우는 카운팅 정렬 - feat.백준(수 정렬하기3)</title>
      <link>https://eenzhd.tistory.com/249</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-16 오후 9.34.13.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Clwn3/btsLkERIaph/T2NiesFsiLhTsUWUkdsuN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Clwn3/btsLkERIaph/T2NiesFsiLhTsUWUkdsuN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Clwn3/btsLkERIaph/T2NiesFsiLhTsUWUkdsuN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FClwn3%2FbtsLkERIaph%2FT2NiesFsiLhTsUWUkdsuN1%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;565&quot; height=&quot;390&quot; data-filename=&quot;스크린샷 2024-12-16 오후 9.34.13.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;816&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;이 문제는 주어진 N개의 수를 오름차순으로 정렬해야 한다. 수의 개수(N)가 최대 10,000,000이지만, 주어진 값이 &lt;b&gt;10,000 이하의 자연수&lt;/b&gt;로 한정적이라는 점에서 &lt;u&gt;카운팅 정렬(Counting Sort)&lt;/u&gt;을 사용할 수 있다. 카운팅 정렬은 정수 데이터의 범위가 제한되어 있을 때 매우 효율적인 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카운팅 정렬의 시간 복잡도는 &lt;u&gt;O(N+K)&lt;/u&gt;이며 K는 값의 범위이다. 따라서 입력 크기가 크더라도 효율적으로 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N = 10&lt;/li&gt;
&lt;li&gt;입력: 5, 2, 3, 1, 4, 2 ,3 ,5, 1, 7&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;카운트 배열 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 범위가 1~10,000이므로 크기가 10,0001인 카운트 배열을 생성한다. 입력된 값의 등장 횟수를 카운트 배열에 저장한다.&lt;/p&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;예를 들면, 처음 num에 들어올 값은 5이고 count[5]의 값을 하나 증가시킨다. 그 다음은 count[2]의 값 증가, 그 다음은 count[3]의 값을 증가시킨다. 8번째 입력을 받았을 때 다시 count[5]의 값이 증가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734352663275&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int n = Integer.valueOf(br.readLine());
int[] count = new int[10001];
for (int i = 0; i &amp;lt; n; i++) {
    int num = Integer.valueOf(br.readLine());
    count[num]++;
}&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;h2 data-ke-size=&quot;size26&quot;&gt;정렬된 결과 출력&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카운트 배열의 각 인덱스를 반복하며 등장 횟수만큼 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count[i]가 0 초과라면 그 숫자를 출력하고 0이라면 출력없이 다음으로 넘어간다.&lt;/p&gt;
&lt;pre id=&quot;code_1734352966626&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt;= 10000; i++) {
    while (count[i] &amp;gt; 0) {
        bw.write(i + &quot;\n&quot;);
        count[i]--;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;숫자&lt;br /&gt;(인덱스)&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;등장 횟수&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count[1] = 2, 값 1이 2번 등장했다는 의미가 되며 출력은 1, 1이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count[2] = 2, 값 2가 2번 등장했다는 의미가 되며 출력은 2, 2가 된다. (1, 1, 2 ,2)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count[3] = 2, 값 3이 2번 등장했다는 의미가 되며 출력은 3, 3이 된다. (1, 1, 2, 2, 3, 3)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count[4] = 1, 값 4가 1번 등장했다는 의미가 되며 출력은 4가 된다. (1, 1, 2, 2, 3, 3, 4)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count[5] = 2, 값 5가 2번 등장했다는 의미가 되며 출력은 5, 5가 된다. (1, 1, 2, 2, 3, 3, 4, 5, 5)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count[6] = 0, 값 6이 0번 등장했다는 의미가 되며 출력이 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count[7] = 1, 값 7이 1번 등장했다는 의미가 되며 출력은 7이 된다. (1, 1, 2, 2, 3, 3, 4, 5, 5, 7)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 이후는 모두 0이므로 출력이 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;전체 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1734353540281&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int n = Integer.valueOf(br.readLine());
        int[] count = new int[10001];
        for (int i = 0; i &amp;lt; n; i++) {
            int num = Integer.valueOf(br.readLine());
            count[num]++;
        }
        for (int i = 0; i &amp;lt;= 10000; i++) {
            while (count[i] &amp;gt; 0) {
                bw.write(i + &quot;\n&quot;);
                count[i]--;
            }
        }
        bw.flush();
        bw.close();
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘</category>
      <category>counting sort</category>
      <category>Java</category>
      <category>백준</category>
      <category>수 정렬하기3</category>
      <category>알고리즘</category>
      <category>카운팅 정렬</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/249</guid>
      <comments>https://eenzhd.tistory.com/249#entry249comment</comments>
      <pubDate>Mon, 16 Dec 2024 21:52:23 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] Java로 배우는 이진탐색 - feat.백준(숫자 카드 2)</title>
      <link>https://eenzhd.tistory.com/248</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;정렬된 배열에서 특정 값의 등장 횟수를 효율적으로 구하는 방법으로 이진탐색을 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진탐색은 정렬된 배열에서 원하는 값을 찾기 위해 검색 범위를 절반으로 줄여나가는 알고리즘이다. 배열에서 중간값을 선택하고, 검색 대상 값이 중간값보다 크거나 작은지에 따라 검색 범위를 좁혀나갈 수 있다. 반복적으로 범위를 줄이다보면 값을 찾거나 값이 존재하지 않음을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진탐색의 시간복잡도는 &lt;u&gt;O(logN)&lt;/u&gt;으로 매우 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-15 오후 5.11.33.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byRqMp/btsLkEQumva/YlO5vvJAK7dPwAdgkcnfW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byRqMp/btsLkEQumva/YlO5vvJAK7dPwAdgkcnfW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byRqMp/btsLkEQumva/YlO5vvJAK7dPwAdgkcnfW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyRqMp%2FbtsLkEQumva%2FYlO5vvJAK7dPwAdgkcnfW1%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;658&quot; height=&quot;579&quot; data-filename=&quot;스크린샷 2024-12-15 오후 5.11.33.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진탐색은 정렬이 된 배열에서 사용해야 하므로 주어진 배열을 우선 정렬해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[6, 3, 2, 10, 10, -10, -10, 7, 3] -&amp;gt; [-10, -10, 2, 3, 3, 6, 10, 10, 10]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾고자 하는 값(target = 10)일 경우 10은 주어진 배열에서 3번 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이진탐색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진탐색을 두 번 사용하여 등장 횟수를 계산한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;lowerBound : target이 처음 등장하는 위치를 찾는다.&lt;/li&gt;
&lt;li&gt;upperBound : target보다 큰 값이 처음 등장하는 위치를 찾는다.&lt;/li&gt;
&lt;li&gt;등장 횟수는 upperBound - lowerBound로 계산한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1734250481104&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 특정 숫자의 등장 횟수를 이진탐색
private static int countOccurrences(int[] nArr, int card) {

    int lower = lowerBound(nArr, card);
    int upper = upperBound(nArr, card);

    return upper - lower;  // tartget의 등장횟수 계산
}&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;h3 data-ke-size=&quot;size23&quot;&gt;lowerBound&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에서 target 값이 처음 등장하는 위치를 반환할 메소드이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 nArr = [-10, -10, 2, 3, 3, 6, 10, 10, 10]에서 lowerBound(10)은 &lt;b&gt;6&lt;/b&gt;을 반환한다.(첫번째 10의 위치)&lt;/p&gt;
&lt;pre id=&quot;code_1734250567486&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// target이 처음으로 등장하는 위치 계산
private static int lowerBound(int[] nArr, int target) {
    int left = 0;
    int right = nArr.length;

    while (left &amp;lt; right) {
        int mid = left + (right - left) / 2;
        if (nArr[mid] &amp;gt;= target) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;upperBound&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에서 target보다 큰 값이 처음 등장하는 위치를 반환할 메소드이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예를 들면 nArr = [-10, -10, 2, 3, 3, 6, 10, 10, 10]에서 upperBound(10)은&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;b&gt;9&lt;/b&gt;를 반환한다.(10이후의 위치)&lt;/p&gt;
&lt;pre id=&quot;code_1734250651831&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// target보다 큰 값이 처음으로 등장하는 위치
private static int upperBound(int[] nArr, int target) {
    int left = 0;
    int right = nArr.length;

    while (left &amp;lt; right) {
        int mid = left + (right - left) / 2;
        if (nArr[mid] &amp;gt; target) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}&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;h2 data-ke-size=&quot;size26&quot;&gt;전체 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1734250847091&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nArr = new int[n];

        for (int i = 0; i &amp;lt; n; i++) {
            nArr[i] = sc.nextInt();
        }

        Arrays.sort(nArr);

        int m = sc.nextInt();
        int[] mArr = new int[m];
        for (int j = 0; j &amp;lt; m; j++) {
            mArr[j] = sc.nextInt();
        }

        StringBuilder sb = new StringBuilder();
        for (int card : mArr) {
            int count = countOccurrences(nArr, card);
            sb.append(count).append(&quot; &quot;);
        }
        System.out.println(sb.toString());
    }

    // 특정 숫자의 등장 횟수를 이진탐색
    private static int countOccurrences(int[] nArr, int card) {

        int lower = lowerBound(nArr, card);
        int upper = upperBound(nArr, card);

        return upper - lower;  // tartget의 등장횟수 계산
    }

    // target이 처음으로 등장하는 위치 계산
    private static int lowerBound(int[] nArr, int target) {
        int left = 0;
        int right = nArr.length;

        while (left &amp;lt; right) {
            int mid = left + (right - left) / 2;
            if (nArr[mid] &amp;gt;= target) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    // target보다 큰 값이 처음으로 등장하는 위치
    private static int upperBound(int[] nArr, int target) {
        int left = 0;
        int right = nArr.length;

        while (left &amp;lt; right) {
            int mid = left + (right - left) / 2;
            if (nArr[mid] &amp;gt; target) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>Java</category>
      <category>백준</category>
      <category>숫자 카드2</category>
      <category>알고리즘</category>
      <category>이진탐색</category>
      <category>정렬</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/248</guid>
      <comments>https://eenzhd.tistory.com/248#entry248comment</comments>
      <pubDate>Sun, 15 Dec 2024 17:21:03 +0900</pubDate>
    </item>
    <item>
      <title>[스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술] 회원 관리 웹 애플리케이션 요구사항</title>
      <link>https://eenzhd.tistory.com/247</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;회원 관리 웹 애플리케이션 요구사항&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/li&gt;
&lt;li&gt;회원 목록 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회원 도메인 모델&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id는 Member를 회원 저장소에 저장하면 회원 저장소가 할당한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734007819732&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package hello.servlet.basic.domain.member;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Member {

    private Long id;
    private String username;
    private int age;

    public Member(){}

    public Member(String username, int age) {
        this.username = username;
        this.age = age;
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;회원 저장소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 저장소는 싱글톤 패턴을 적용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링을 사용하면 스프링 빈으로 등록하면 되지만 지금은 순수 서블릿만으로 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴은 객체를 단 한번만 생성해서 공유해야 하므로 생성자는 &lt;u&gt;private&lt;/u&gt; 접근자로 막는다.&lt;/p&gt;
&lt;pre id=&quot;code_1734007880207&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package hello.servlet.basic.domain.member;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 동시성 문제가 고려되어 있지 않음.
 * 실무에서는 ConcurrentHashMap이나 AtomicLong 사용 고려.
 */
public class MemberRepository {

    private static Map&amp;lt;Long, Member&amp;gt; store = new HashMap&amp;lt;&amp;gt;(); //static 사용
    private static long sequence = 0L; //static 사용
    private static final MemberRepository instance = new MemberRepository();

    public static MemberRepository getInstance() {
        return instance;
    }

    private MemberRepository() {
    }

    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    public Member findById(Long id) {
        return store.get(id);
    }

    public List&amp;lt;Member&amp;gt; findAll() {
        return new ArrayList&amp;lt;&amp;gt;(store.values());
    }

    public void clearStore() {
        store.clear();
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;회원 저장소 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원을 저장하고 목록을 조회하는 테스트를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 테스트가 끝날 때 다음 테스트에 영향을 주지 않도록 각 테스트의 저장소를 clearStore()를 호출하여 초기화 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734007983841&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package hello.servlet.domain.member;

import hello.servlet.basic.domain.member.Member;
import hello.servlet.basic.domain.member.MemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class MemberRepositoryTest {

    MemberRepository memberRepository = MemberRepository.getInstance();

    @AfterEach
    void afterEach() {
        memberRepository.clearStore();
    }

    @Test
    void save() {
        // given
        Member member = new Member(&quot;hello&quot;, 20);

        // when
        Member savedMember = memberRepository.save(member);

        // then
        Member findMember = memberRepository.findById(savedMember.getId());
        assertThat(findMember).isEqualTo(savedMember);
    }

    @Test
    void findAll() {
        // given
        Member member1 = new Member(&quot;member1&quot;, 20);
        Member member2 = new Member(&quot;member2&quot;, 30);

        memberRepository.save(member1);
        memberRepository.save(member2);

        // when
        List&amp;lt;Member&amp;gt; result = memberRepository.findAll();

        // then
        assertThat(result.size()).isEqualTo(2);
        assertThat(result).contains(member1, member2);
    }
}&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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;인프런 김영한님 강의&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;인프런의 김영한님 스프링 강의&lt;/b&gt;&lt;/u&gt;를 보고 작성한 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;강의를 들으면서 정리한 글이므로 틀린 내용이나 오타가 있을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-End/Spring</category>
      <category>MVC</category>
      <category>spring</category>
      <category>김영한</category>
      <category>스프링</category>
      <category>회원관리</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/247</guid>
      <comments>https://eenzhd.tistory.com/247#entry247comment</comments>
      <pubDate>Thu, 12 Dec 2024 21:54:23 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 옵셔널(Optional)</title>
      <link>https://eenzhd.tistory.com/244</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;옵셔널(Optional)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift가 가진 가장 큰 특징 중 하나가 바로 옵셔널(Optional)이다. 이 옵셔널은 값이 있을 수도 있고 없을 수도 있다는 것을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문자열의 값이 있으면 &quot;가나다&quot;가 되지만, 값이 없다면 &quot;&quot;가 될까? 답은 아니다. &quot;&quot;도 엄연히 값이 있는 문자열이라고 할 수 있다. 값이 없는 문자열을&amp;nbsp;&lt;u&gt;&lt;b&gt;nil&lt;/b&gt;&lt;/u&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예를 들어 정수형의 값이 있으면 100과 같은 값이 존재할 것이다. 값이 없다면 0이 될까? 0은 0이라는 숫자 '값'이기 때문에 값이 없는 정수 또한 &lt;u&gt;&lt;b&gt;nil&lt;/b&gt;&lt;/u&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 변수에 nil을 넣을 수 있는 것은 아니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728548071895&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name: String = &quot;Soo&quot;
name = nil&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-10 오후 5.14.59.png&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lnfaY/btsJ1PMQaAe/F8K3z37dhoTYm6GbK1GLC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lnfaY/btsJ1PMQaAe/F8K3z37dhoTYm6GbK1GLC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lnfaY/btsJ1PMQaAe/F8K3z37dhoTYm6GbK1GLC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlnfaY%2FbtsJ1PMQaAe%2FF8K3z37dhoTYm6GbK1GLC0%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;620&quot; height=&quot;57&quot; data-filename=&quot;스크린샷 2024-10-10 오후 5.14.59.png&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;'nil' cannot be assigned to type 'String'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728548315476&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var email: String?
print(email)  // nil

email = &quot;abcd@gmail.com&quot;
print(email)  // Optional(&quot;abcd@gmail.com&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;값이 있을 수도 있고 없을 수도 있는 변수를 정의할 때는 타입 어노테이션에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;u&gt;&lt;b&gt;?&lt;/b&gt;&lt;/u&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 붙여야 한다. 이렇게 정의한 변수를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;u&gt;옵셔널(Optional)&lt;/u&gt;이라고 하며 옵셔널에 초기값을 지정하지 않으면 기본값은 nil이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728548454479&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let optionalEmail: String? = &quot;abcd@gmail.com&quot;
let requiredEmail: String = optionalEmail  // Compile Error!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-10 오후 5.20.34.png&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MiZ2S/btsJ0f61EQ8/skyHnQT5r3XkKoAE5Yp6q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MiZ2S/btsJ0f61EQ8/skyHnQT5r3XkKoAE5Yp6q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MiZ2S/btsJ0f61EQ8/skyHnQT5r3XkKoAE5Yp6q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMiZ2S%2FbtsJ0f61EQ8%2FskyHnQT5r3XkKoAE5Yp6q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;881&quot; height=&quot;50&quot; data-filename=&quot;스크린샷 2024-10-10 오후 5.20.34.png&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Value of optional type 'String?' must be unwrapped to a vlaue of type 'String'&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;옵셔널로 정의한 변수는 옵셔널이 아닌 변수와 다르기 때문에 위처럼 사용할 수 없다. requiredEmail 변수는 옵셔널이 아닌 String 타입이기 때문에 항상 값을 가지고 있어야 한다. optionalEmail은 옵셔널 변수이기 때문에 실제 코드가 실행되기 전까지는 값이 있는지 없는지 알 수 없다.&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;따라서 Swift 컴파일러는 안전을 위해 requiredEmail에는 옵셔널로 선언된 변수를 대입할 수 없게 만들었다.&lt;/p&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 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;옵셔널 바인딩(Optional Binding)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널의 값을 가져오고 싶을 경우에 사용하는 것이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;옵셔널 바인딩&lt;/u&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널 바인딩은 옵셔널의 값이 존재하는지 검사한 뒤, 값이 존재한다면 그 값을 다른 변수에 대입시키는 역할을 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;if let&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;if var&lt;/b&gt;를 사용하는데 옵셔널의 값을 벗겼을 때 값이 있다면 if문 안으로 들어가고 값이 nil이라면 통과한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728622475155&quot; class=&quot;gauss&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;if let email = optionalEmail {
    print(email)  // optionalEmail의 값이 존재할 경우 해당 값 출력
}

// optionalEmail 값이 존재하지 않을 경우 if문 통과&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;하나의 if문에서 콤마( , )로 구분하여 여러 옵셔널을 바인딩 할 수 있고, 모든 옵셔널의 값이 존재해야 if문 안으로 들어간다.&lt;/p&gt;
&lt;pre id=&quot;code_1728622475156&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;var optionalName: String? = &quot;Soo&quot;
var optionalEmail: String? = &quot;abcd@gmail.com&quot;

if let name = optionalName, let email = optionalEmail {
    // name과 email의 값이 존재해야 들어온다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;깃북에서는 let name = ... , email 로 작성되어 있는데&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Expected 'let' in conditional&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에러가 발생한다.&lt;br /&gt;각 optional에 대해 let을 사용하면 에러가 발생하지 않았다.&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;코드가 너무 길 경우에는 아래와 같이 여러 줄에 걸쳐서 작성할 수 있으며, 두 번째 let부터는 생략이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1728622475157&quot; class=&quot;nix&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;if let name = optionalName,
   let email = optionalEmail {
    // name과 email의 값이 존재해야 들어온다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 아래 코드와도 동일하다.&lt;/p&gt;
&lt;pre id=&quot;code_1728622475158&quot; class=&quot;nix&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;if let name = optionalName {
    if let email = optionalEmail {
        // name과 email의 값이 존재해야 들어온다.
    }
}&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;한 번의 if문에서 여러 옵셔널을 바인딩할 수 있게 된 것은 Swift 1.2 버전부터이다. 이전 버전까지는 바로 위와 같이 여러 번으로 감싸진 옵셔널 바인딩을 사용했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널을 바인딩할 때 ( , )를 사용해서 조건도 함께 지정할 수 있다. 콤마 이후의 조건절은 옵셔널 바인딩이 일어난 후에 실행되며 즉, 옵셔널이 벗겨진 값을 가지고 조건을 검사하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728622475158&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;var optionalAge: Int? = 20

if let age = optionalAge, age &amp;gt;= 20 {
    // age 값이 존재하고 age가 20 이상일 경우 진입
}

// 또는

if let age = optionalAge {
    if age &amp;gt;= 20 {
        // age 값이 존재하고 age가 20 이상일 경우 진입
    }
}&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;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;옵셔널 체이닝(Optional Chaining)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널로 선언한 어떤 배열이 있을 때, 이 배열이 빈 배열인지 아닌지 검사하려면 어떻게 해야할까?&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728633679032&quot; class=&quot;vbscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;let array: [String]? = []
var isEmptyArray = false

if let array = array, array.isEmpty {
    isEmptyArray = true
} else {
    isEmptyArray = false
}

isEmptyArray&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;nil이 아니면서 빈 배열인지 아닌지를 확인해보면 되는데 위 코드를 옵셔널 체이닝을 사용하면 간결하게 쓸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1728633679034&quot; class=&quot;vbscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;let array: [String]? = []

let isEmptyArray = array?.isEmpty == true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널 체이닝은 옵셔널의 속성에 접근할 때, 옵셔널 바인딩 과정을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;?&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;array?.isEmpty의 결과로 나올 수 있는 값은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;nil, true, false&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;3개이다. isEmpty의 반환값은 Bool인데 옵셔널 체이닝으로 인해 Bool?을 반환하도록 바뀐 것이다. 따라서 값이 실제로 true인지 확인하려면 == true를 해주어야 한다.&lt;/p&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 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;옵셔널 벗기기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널을 사용할 때마다 옵셔널 바인딩을 하는 것이 제일 좋지만 개발을 하다보면 값이 분명히 존재할 것임에도 불구하고 옵셔널로 사용해야 하는 불편함이 있을 수 있다. 이런 경우에는 옵셔널에 값이 있다고 가정하고 바로 접근할 수 있도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;!&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드를 붙이면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728633679035&quot; class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;print(optionalEmail)  // Optional(&quot;abcd@gmail.com&quot;)
print(optionalEmail!)  // abcd@gamil.com&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;! 를 사용할 때 주의할 점은, 옵셔널의 값이 nil인 경우에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;런타임 에러&lt;/b&gt;가 발생한다. Java의 NullPointerException과 비슷하다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728633679036&quot; class=&quot;dart&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;var optionalEmail2: String?
print(optionalEmail2!)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-11 오후 2.28.25.png&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uBanc/btsJ3LwORFg/Ls9TlxRlMWdbxSVjO0ZkkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uBanc/btsJ3LwORFg/Ls9TlxRlMWdbxSVjO0ZkkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uBanc/btsJ3LwORFg/Ls9TlxRlMWdbxSVjO0ZkkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuBanc%2FbtsJ3LwORFg%2FLs9TlxRlMWdbxSVjO0ZkkK%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;1071&quot; height=&quot;111&quot; data-filename=&quot;스크린샷 2024-10-11 오후 2.28.25.png&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;fatal error: Unexpectedly found nil while unwrapping an Optional value&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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;출처:&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://devxoul.gitbooks.io/ios-with-swift-in-40-hours/content/Chapter-2/optionals.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;전수열님 GitBook&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;u&gt;&lt;b&gt;전수열님&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;u&gt;&lt;b&gt;의 깃북&lt;/b&gt;&lt;/u&gt;을 참고하여 작성한 글입니&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>ios</category>
      <category>NIL</category>
      <category>optional</category>
      <category>Swift</category>
      <category>스위프트</category>
      <category>옵셔널</category>
      <category>옵셔널 벗기기</category>
      <category>옵셔널바인딩</category>
      <category>옵셔널체이닝</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/244</guid>
      <comments>https://eenzhd.tistory.com/244#entry244comment</comments>
      <pubDate>Fri, 11 Oct 2024 17:32:07 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 조건문과 반복문</title>
      <link>https://eenzhd.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;조건문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 검사할 때에는 if, switch를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728364884046&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var age = 20
var student = &quot;&quot;

if age &amp;gt;= 8 &amp;amp;&amp;amp; age &amp;lt; 14 {
    student = &quot;학생&quot;
} else if age &amp;lt; 17 {
    student = &quot;중학생&quot;
} else if age &amp;lt; 20 {
    student = &quot;대학생&quot;
} else {
    student = &quot;기타&quot;
}

student    // 기타&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;if문의 조건절에는 값이 정확하게 참, 거짓으로 나오는 Bool타입을 사용해야 한다. Swift는 타입 검사를 엄격하게 하기 때문에 다른 언어에서는 사용 가능한 &lt;u&gt;!number&lt;/u&gt;&amp;nbsp;같은 코드는 사용하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-08 오후 2.23.14.png&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVjV8X/btsJYWlqNgp/kcnp4jDxEjhBfMM56is54K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVjV8X/btsJYWlqNgp/kcnp4jDxEjhBfMM56is54K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVjV8X/btsJYWlqNgp/kcnp4jDxEjhBfMM56is54K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVjV8X%2FbtsJYWlqNgp%2Fkcnp4jDxEjhBfMM56is54K%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;713&quot; height=&quot;75&quot; data-filename=&quot;스크린샷 2024-10-08 오후 2.23.14.png&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;!number 대신 number == 0을 사용하면 컴파일 에러가 나지 않는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-08 오후 2.24.35.png&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9EjtX/btsJYHvdC6Y/vWU6PB0fgTfdZDnbPZvjY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9EjtX/btsJYHvdC6Y/vWU6PB0fgTfdZDnbPZvjY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9EjtX/btsJYHvdC6Y/vWU6PB0fgTfdZDnbPZvjY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9EjtX%2FbtsJYHvdC6Y%2FvWU6PB0fgTfdZDnbPZvjY0%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;711&quot; height=&quot;148&quot; data-filename=&quot;스크린샷 2024-10-08 오후 2.24.35.png&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;148&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;빈 문자열이나 배열 등을 검사할 때도 길이가 명확하게 0인지 아닌지를 검사해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728365122223&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if name.isEmpty { ... }
if languages.isEmpty { ... }&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;h2 data-ke-size=&quot;size26&quot;&gt;반복문&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;switch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift의 switch구문은 Java와는 다르게 패턴 매칭이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 작성했던 if문을 switch문으로 변경해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1728365257595&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;switch age {
case 8..&amp;lt;14:
    student = &quot;초등학생&quot;
case 14..&amp;lt;17:
    student = &quot;중학생&quot;
case 17..&amp;lt;20:
    student = &quot;고등학생&quot;
default:
    student = &quot;기타&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;8..&amp;lt;14와 같이 범위(Range)안에 age가 포함되었는지의 여부를 검사할 수 있다.&lt;/p&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;for&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 연산을 할 때는 for문이나 while문을 사용한다. for구문을 사용해서 배열과 딕셔너리를 차례로 순환할 때는 아래와 같이 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728365662145&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for language in languages {
    print(&quot;저는 \(language) 언어를 다룰 수 있습니다.&quot;)
}

for (country, capital) in capitals {
    print(&quot;\(country)의 수도는 \(capital)입니다.&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-08 오후 2.34.45.png&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3TvIp/btsJXxNWSVZ/J3bBT4AgJTyk8R1fXB4Zzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3TvIp/btsJXxNWSVZ/J3bBT4AgJTyk8R1fXB4Zzk/img.png&quot; data-alt=&quot;for문 출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3TvIp/btsJXxNWSVZ/J3bBT4AgJTyk8R1fXB4Zzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3TvIp%2FbtsJXxNWSVZ%2FJ3bBT4AgJTyk8R1fXB4Zzk%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;408&quot; height=&quot;283&quot; data-filename=&quot;스크린샷 2024-10-08 오후 2.34.45.png&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;for문 출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;단순한 반복문을 만들고 싶다면 범위를 만들어서 반복할 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728365764696&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in 0..&amp;lt;10 {
    i
}&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;만약 i를 사용하지 않고 단순한 반복을 하고 싶다면 &lt;u&gt;i 대신 _를 사용&lt;/u&gt;해서 무시할 수도 있다. _ 키워드는 어디서나 변수 이름 대신에 유용하게 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1728365849233&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for _ in 0..&amp;lt;10 {
    print(&quot;Hello!&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;while&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while은 조건문의 값이 true일 때 계속 반복된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728365921022&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var i = 0
while i &amp;lt; 100 {
    i += 1
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 while문을 작성하면 i가 100이 되기 전까지 계속 반복된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i는 0에서 시작해서 1씩 누적되고 100이 되면 while문을 벗어난다.&lt;/p&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;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;출처:&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://devxoul.gitbooks.io/ios-with-swift-in-40-hours/content/Chapter-2/control-flow.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;전수열님 GitBook&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;u&gt;&lt;b&gt;전수열님&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;u&gt;&lt;b&gt;의 깃북&lt;/b&gt;&lt;/u&gt;을 참고하여 작성한 글입니&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>for문</category>
      <category>if문</category>
      <category>Swift</category>
      <category>while문</category>
      <category>반복문</category>
      <category>스위프트</category>
      <category>조건문</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/243</guid>
      <comments>https://eenzhd.tistory.com/243#entry243comment</comments>
      <pubDate>Thu, 10 Oct 2024 15:39:58 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 변수와 상수</title>
      <link>https://eenzhd.tistory.com/242</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;변수와 상수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수(variable)는 값을 수정할 수 있고 상수(constant)는 값을 수정할 수 없기 때문에 Swift에서는 안전하게 &lt;b&gt;상수&lt;/b&gt;를 사용하는 것을 권장하고 있다. 변수는 &lt;u&gt;var&lt;/u&gt;로 선언하고, 상수는 &lt;u&gt;let&lt;/u&gt;으로 선언한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728353837663&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name = &quot;Soo&quot;
let birthYear = 2024&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;var로 선언한 name은 이름을 바꾸고 싶을 때 바꿀 수 있지만, 태어난 해를 바꾸려고 할 땐 컴파일 에러가 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728354054393&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name = &quot;Sooo&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1728354109081&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;birthYear = 2023  // Compile Error!&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Cannot assign to value: 'birthYear' is a 'let' constant&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;let 키워드로 선언된 상수의 값을 변경할 수 없다는 에러이므로 바뀌면 안 되는 값은 상수로 정의해두자.&lt;/p&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;Swift는 변수나 상수를 정의할 때 그 자료형이 &lt;b&gt;어떤 타입인지 명시&lt;/b&gt;해주어야 하는 &lt;b&gt;정적 타이핑 언어&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre id=&quot;code_1728354259905&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name: String = &quot;Soo&quot;
let birthYear: Int = 2024
var height: Float = 50.7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 또는 상수 이름 뒤에 콜론( : )을 붙이고 자료형을 써주면 되는데, &lt;u&gt;: String&lt;/u&gt;과 &lt;u&gt;: Int&lt;/u&gt; 등을 가지고 타입 어노테이션(Type Annotation)이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift는 타입을 엄격히 다루기 때문에 다른 자료형끼리는 기본적인 연산조차 되지 않는다. 아래와 같이 Int타입과 Float 타입을 더하려고 하면 컴파일 에러가 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728354412233&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;birthYear + height  // Compile Error!&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Binary operator '+' cannot be applied to operands of type 'Int' and 'Float'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우 아래와 같이 명확하게 사용해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728354464880&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Float(birthYear) + height  // 2074.7

String(birthYear) + &quot;년에 태어난 &quot; + name + &quot;입니다!&quot;   // 2024년에 태어난 Soo입니다!
&quot;\(birthYear)년에 태어난 \(name)입니다!&quot;  // 2024년에 태어난 Soo입니다!&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;h3 data-ke-size=&quot;size23&quot;&gt;타입 추론(Type Inference)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 컴파일러는 큰 따옴표( &quot; )로 감싸진 텍스트는 String 타입인 것을 알고, 숫자는 Int 타입인 것을 인식한다. 타입을 직접 명시하지 않고도 값을 가지고 정적 타이핑을 할 수 있게 해주는 것을 &lt;b&gt;타입 추론(Type Inference)&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열(Array)과 딕셔너리(Dictionary)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열과 딕셔너리는 모두 대괄호( [ ] )를 이용해서 정의할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728355027489&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var languages = [&quot;Swift&quot;, &quot;Java&quot;, &quot;Objective-C&quot;]
var capitals = [
    &quot;한국&quot;: &quot;서울&quot;,
    &quot;일본&quot;: &quot;도쿄&quot;,
    &quot;중국&quot;: &quot;베이징&quot;
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열과 딕셔너리에 접근하거나 값을 변경할 때도 대괄호를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728355157037&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;languages[0]  // Swift
languages[1] = &quot;Python&quot;

capitals[&quot;한국&quot;]  // 서울
capitals[&quot;프랑스&quot;]  = &quot;파리&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;배열과 딕셔너리도 let으로 정의한다면 값을 추가/수정/삭제 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열과 딕셔너리는 대괄호 안에 어떤 타입을 받을 것인지 명시한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728355285300&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var languages: [String] = [&quot;Swift&quot;, &quot;Java&quot;, &quot;Objective-C&quot;]
var capitals: [String: String] = [
    &quot;한국&quot;: &quot;서울&quot;,
    &quot;일본&quot;: &quot;도쿄&quot;,
    &quot;중국&quot;: &quot;베이징&quot;
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 배열이나 빈 딕셔너리 또한 대괄호를 사용해 타입을 명시한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728355430666&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var languages: [String] = []
var capitals: [String: String] = [:]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 배열로 선언하는 것을 조금 더 간결하게 하고자 한다면 아래와 같이 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728355512460&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var languages = [String]()
var capitals = [String: String]()&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;타입 뒤에 괄호()를 쓰는 것은 생성자(initializer)를 호출하는 것이다.&lt;/p&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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;출처:&lt;span&gt; &lt;/span&gt;&lt;a href=&quot;https://devxoul.gitbooks.io/ios-with-swift-in-40-hours/content/Chapter-2/variables-and-constants.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;전수열님 GitBook&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;u&gt;&lt;b&gt;전수열님&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;u&gt;&lt;b&gt;의 깃북&lt;/b&gt;&lt;/u&gt;을 참고하여 작성한 글입니&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>ios</category>
      <category>Swift</category>
      <category>변수</category>
      <category>상수</category>
      <category>스위프트</category>
      <category>타입추론</category>
      <author> 뚜비</author>
      <guid isPermaLink="true">https://eenzhd.tistory.com/242</guid>
      <comments>https://eenzhd.tistory.com/242#entry242comment</comments>
      <pubDate>Tue, 8 Oct 2024 13:17:00 +0900</pubDate>
    </item>
  </channel>
</rss>