<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>상혁몬</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 19 Mar 2025 08:21:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>상혁몬</title>
            <url>https://velog.velcdn.com/images/sanghyeok_moon/profile/8f91cf33-c490-4004-ae1c-624b41e65712/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 상혁몬. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sanghyeok_moon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[예외 처리]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC-elp3r37v</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC-elp3r37v</guid>
            <pubDate>Wed, 19 Mar 2025 08:21:51 GMT</pubDate>
            <description><![CDATA[<p>예외를 처리하기 위해 try ~ catch, throws 구문을 이용.
예외 처리 방법을 알게 되면 안전하고 유연한 프로그래밍 구사 가능</p>
<hr>
<h3 id="예외-처리하기">예외 처리하기</h3>
<p><em>try~catch 기본 구조</em></p>
<pre><code class="language-java">try {
    (수행할 문장 1);
    (수행할 문장 2);
    ...
} catch(예외 1) {
    (수행할 문장 1);
    ...
} catch(예외 2) {
    (수행할 문장 1);
    ...
}</code></pre>
<p>try 문 안의 수행할 문장에서 예외가 발생하지 않는다면 catch 문에 문장들은 수행되지 않는다. 하지만 try 문 안의 문장을 수행하는 도중 예외 발생하면 예외에 해당되는 catch 문이 수행됨
<em>숫자를 0을로 나누었을 때 발생하는 예외를 처리</em></p>
<pre><code class="language-java">public class Sample {
    public static void main(String[] args) {
        int c;
        try {
            c = 4 / 0;
        } catch(ArithmeticException e) {
            c = -1; // 예외가 발생해 이 문장이 수행됨
        }
    }
}</code></pre>
<p>ArithmeticException e에서 e는 ArithmeticException 클래스의 객체, 즉 예외 객체에 해당한다.
이 예외 객체를 통해 해당 예외 클래스의 변수나 메서드를 호출할 수도 있다.</p>
<hr>
<h3 id="finally">finally</h3>
<p>어떤 예외가 발생하더라도 반드시 실행되어야 하는 부분이 있다면, <code>finally</code> 문을 사용한다.</p>
<pre><code class="language-java">public class Sample {
    public void shouldBeRun() {
        System.out.println(&quot;ok thanks&quot;);
    }

       public static void main(String[] args) {
        Sample sample = new Sample();
        int c;
        try {
            c = 4 / 0;
        } catch (ArtihmeticException e) {
             c = -1;
        } finally {
            sample.shouldBeRun(); // 예외에 상관없이 무조건적으로 실행
        }
    }
}</code></pre>
<pre><code>ok, thanks</code></pre><p><code>finally</code>문은 try 문장 수행 중 예외 발생 여부에 상관없이 무조건 실행된다.
따라서 코드를 실행하면 <code>sample.shouldBeRun()</code> 메서드가 실행됨</p>
<hr>
<h3 id="예외-활용하기---runtimeexception과-exception">예외 활용하기 - RuntimeException과 Exception</h3>
<blockquote>
<p><strong>예외</strong>는 크게 두 가지로 구분된다</p>
</blockquote>
<ol>
<li><strong>RuntimeException</strong> : 실행 시 발생하는 예외</li>
<li><strong>Exception</strong> : 컴파일 시 발생하는 예외</li>
</ol>
<h4 id="runtimeexception">RuntimeException</h4>
<pre><code class="language-java">class FoolException extends RuntimeException{
}

public class Sample {
    public void sayMoon(String moon) {
        if(&quot;바보&quot;.equals(moon)) {
            throw new FoolException();
        }
        System.out.println(&quot;당신의 별명은 &quot;+moon+&quot; 입니다.&quot;);
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayMoon(&quot;바보&quot;);
        sample.sayMoon(&quot;야호&quot;);
    }
}</code></pre>
<p>이 프로그램을 실행하면 &#39;바보&#39;라는 입력 값으로 sayMoon 메서드 실행 시 다음과 같은 예외가 발생한다.</p>
<pre><code class="language-java">Exception in thread &quot;main&quot; FoolException
...</code></pre>
<h4 id="exception">Exception</h4>
<p>RuntimeException을 상속하는 FoolException을 Exception 상속으로 변경하기만 하면 Sample 클래스에서 컴파일 오류가 발생한다.
이유는 예측 가능한 CheckedException으로 변경되어 예외 처리를 컴파일러가 강제하기 때문.
따라서 try~catch문을 통해 컴파일 오류를 막을 수 있다.</p>
<pre><code class="language-java">class FoolException extends Exception {}

public class Sample {
    public void sayMoon(String moon) {
        try {
            if(&quot;바보&quot;.equals(moon)) {
                throw new FoolException();
            }
            System.out.println(&quot;당신의 별명은 &quot;+moon+&quot; 입니다.&quot;);
        } catch (FoolException e) {
            System.out.println(&quot;FoolException이 발생했습니다.&quot;);        
        }
     }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayMoon(&quot;바보&quot;);
        sample.sayMoon(&quot;야호&quot;);
    }
}</code></pre>
<hr>
<h3 id="예외-던지기">예외 던지기</h3>
<p>앞 예제에서 sayMoon() 메서드에서 FoolException을 발생시키고 예외 처리도 sayMoon() 메서드에서 했다.
하지만 이렇게 하지 않고 sayMoon()메서드를 호출한 곳에서 FoolException을 처리하도록 예외를 위로 던질 수 있는 방법이 있다.</p>
<p><em>예외를 위로 던지는 예시</em></p>
<pre><code class="language-java">public class Sample {
    public void sayMoon(String moon) throws FoolException {
        if(&quot;바보&quot;.equals(moon)) {
            throw new FoolException();
        }
        System.out.println(&quot;당신의 별명은 &quot;+moon+&quot; 입니다.&quot;);
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        try {
            sample.sayMoon(&quot;바보&quot;);
            sample.sayMoon(&quot;야호&quot;);
        } catch (FoolException e) {
            System.out.println(&quot;FoolException이 발생했습니다.&quot;);
        }
    }
}</code></pre>
<p>main 메서드에서 try~catch 문으로 sayMoon 메서드에 대한 FoolException 예외를 처리하였다.</p>
<blockquote>
<p>FoolException 처리를 sayMoon() 메서드에서 하는 것이 좋을까? 아니면 throws를 이용하여 예외 처리를 main 메서드에서 하는 것이 좋을까?</p>
</blockquote>
<h3 id="예외-처리-방식의-차이">예외 처리 방식의 차이</h3>
<p>sayMoon() 메서드에서 처리하는 것과 main() 메서드에서 예외 처리 하는 것에는 아주 큰 차이가 존재한다.</p>
<ul>
<li>sayMoon() 메서드에서 예외를 처리하는 경우 바보, 야호 두 문장이 모두 수행된다.
<code>sample.sayMoon(&quot;바보&quot;)</code> 문장 수행 시 FoolException이 발생하겠지만 그 다음 문장인 <code>sample.sayMoon(&quot;야호&quot;)</code> 역시 수행된다.</li>
<li>하지만 main() 메서드에서 예외 처리를 한 경우에는 두 번째 문장인 <code>sample.sayMoon(&quot;야호&quot;)</code>가 수행되지 않는다.
왜냐면 이미 첫 번째 문장에서 예외가 발생해 catch 문으로 빠져버리기 때문<pre><code class="language-java">try {
  sample.sayMoon(&quot;바보&quot;);
  sample.sayMoon(&quot;야호&quot;); // 이 문장 수행 x
} catch (FoolException e) {
  System.out.println(&quot;FoolException이 발생했습니다.&quot;);
}</code></pre>
</li>
<li><em>예외 처리를 하는 위치는 매우 중요*</em>
프로그램 수행 여부를 결정하고, 트랜잭션 처리와도 매우 밀접한 관계가 있다.</li>
</ul>
<hr>
<h3 id="정리">정리</h3>
<h4 id="1-예외의-종류">1. 예외의 종류</h4>
<p>(1) Checked Exception (컴파일 타임 예외)</p>
<ul>
<li>컴파일러가 체크하는 예외 (반드시 처리 필요)</li>
<li>Exception 클래스의 직계 하위 클래스 (ex: IOException, SQLException)</li>
<li>예시: 파일을 읽을 때 파일이 없을 경우 (FileNotFoundException)</li>
</ul>
<p>(2) Unchecked Exception (런타임 예외)</p>
<ul>
<li>컴파일러가 체크하지 않는다 (처리 선택적)</li>
<li><code>RuntimeException</code>의 하위 클래스(ex: <code>NullPointerException</code>, <code>ArrayIndexOutOfBoundsException</code>)</li>
<li>예시: 배열 범위 초과, <code>null</code> 객체 접근</li>
</ul>
<p>(3) Error</p>
<ul>
<li>시스템 레벨의 심각한 문제, 개발자가 처리할 수 없고, 애플리케이션 종료 유발</li>
</ul>
<hr>
<h4 id="2-예외-처리-기본-문법">2. 예외 처리 기본 문법</h4>
<pre><code class="language-java">try {
    // 예외가 발생할 수 있는 코드
    FileReader file = new FileReader(&quot;test.txt&quot;);
} 
catch (FileNotFoundException e) {
    // 특정 예외 처리 (여러 개 사용 가능)
    System.out.println(&quot;파일을 찾을 수 없습니다!&quot;);
} 
catch (Exception e) {
    // 일반 예외 처리 (가장 마지막에 위치)
    e.printStackTrace();
} 
finally {
    // 예외 발생 여부와 무관하게 실행 (리소스 정리)
    System.out.println(&quot;항상 실행됩니다.&quot;);
}</code></pre>
<hr>
<h4 id="3-주요-키워드">3. 주요 키워드</h4>
<ul>
<li><p><code>throw</code> : <strong>예외를 강제로 발생</strong> 시키는 키워드</p>
<pre><code class="language-java">if (balance &lt; 0) {
  throw new IllegalArgumentException(&quot;잔액은 음수일 수 없습니다.&quot;);
}</code></pre>
</li>
<li><p><code>throws</code> : <strong>예외를 호출자에게 전파</strong></p>
<pre><code class="language-java">public void readFile() throws IOException {
  // 파일 읽기 로직
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스태틱]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%8A%A4%ED%83%9C%ED%8B%B1</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%8A%A4%ED%83%9C%ED%8B%B1</guid>
            <pubDate>Tue, 18 Mar 2025 12:58:49 GMT</pubDate>
            <description><![CDATA[<p><strong>스태틱(static)은 클래스에서 공유되는 변수나 메서드를 정의할 때 사용</strong></p>
<hr>
<h3 id="static-변수-클래스-변수">static 변수 (클래스 변수)</h3>
<pre><code class="language-java">class HouseMoon {
    static String lastName = &quot;문&quot;;
}

public class Sample {
    public static void main(String[] args) {
        HouseMoon moon1 = new HouseMoon();
        HouseMoon moon2 = new HouseMoon();
    }
}</code></pre>
<p>lastName 필드는 어떤 객체이든지 동일한 값인 &#39;문&#39;이다.
<strong>항상 값이 변하지 않는다면 static을 사용해 메모리 낭비를 줄일 수 있다.</strong>
static 키워드를 붙이면 자바는 메모리 할당을 딱 한 번만 하게돼 메모리를 적게 사용할 수 있음.</p>
<blockquote>
<p>lastName값이 변경되지 않기를 바란다면 final static 키워드를 사용.
자바에서는 상수를 의미한다.</p>
</blockquote>
<p><strong>static을 사용하는 또 다른 이유는 값을 공유할 수 있기 때문</strong>. static으로 설정하면 같은 메모리 주소만을 바라보기에 static 변수의 값을 공유하게 된다.</p>
<pre><code class="language-java">class Counter {
    static int count = 0;
    Couter() {
        count++;
        System.out.println(count);
    }
}

public class Sample {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
    }
}</code></pre>
<pre><code>1
2</code></pre><p><code>static</code> 키워드를 붙였더니 count 값이 공유되어 count가 증가되어 출력</p>
<hr>
<h3 id="static-메서드-클래스-메서드">static 메서드 (클래스 메서드)</h3>
<p>static 키워드가 메서드 앞에 붙으면 static 메서드가 됨.</p>
<pre><code class="language-java">class Counter {
    static int count = 0;
    public static int getCount() {
        return count;
    }
}

public class Sample {
    System.out.println(Counter.getCount()); // 스태틱 메서드는 클래스를 이용하여 호출
}</code></pre>
<p>메서드 앞에 static 키워드를 붙이면 <code>Counter.getCount()</code>와 같이 객체 생성 없이도 클래스를 통해 메서드를 직접 호출 가능.
<strong>static method 안에서는 인스턴스 변수 접근 불가능.
위의 코드는 클래스 변수이기에 static method에서 접근이 가능했다.</strong></p>
<hr>
<h3 id="싱글톤-패턴">싱글톤 패턴</h3>
<p>자바의 디자인 패턴 중 하나인 <strong>싱글톤(singleton)</strong>.
싱글톤은 <strong>단 하나의 객체만을 생성하게 강제하는 디자인 패턴</strong>
다시 말해, <strong>클래스를 통해 생성할 수 있는 객체가 한 개만 되도록 만드는 것</strong></p>
<blockquote>
<p>디자인 패턴은 소프트웨어 설계에서 반복적으로 나타나는 문제들을 효과적으로 해결하는 데 사용되는 검증된 설계 방법론</p>
</blockquote>
<pre><code class="language-java">class Singleton {
    private static Singleton one;
    private Singleton() {
    }

    public static Singleton getInstance() {
        if (one == null) {
            one = new Singleton();
        }
        return one;
    }
}

public class Sample {
    public static void main(Stirng[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1 == singleton2); // true
    }
}</code></pre>
<p>Singleton 클래스에 one이라는 static 변수를 작성하고, getInstance() 메서드에서 one값이 null인 경우에만 객체를 생성하도록 해 one 객체가 딱 한 번만 만들어지도록 함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[접근 제어자]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4%EC%9E%90</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4%EC%9E%90</guid>
            <pubDate>Tue, 18 Mar 2025 12:31:32 GMT</pubDate>
            <description><![CDATA[<p><strong>접근 제어자(access modifier)를 사용하여 변수나 메서드의 사용 권한을 설정할 수 있다.</strong></p>
<ul>
<li>private</li>
<li>default</li>
<li>protected</li>
<li>public</li>
</ul>
<p>접근 제어자는 <code>private &lt; default &lt; protected &lt; public</code>순으로 많은 접근을 허용</p>
<hr>
<h3 id="private">private</h3>
<p>접근 제어자가 private으로 설정되어있으면 private이 붙은 변수나 메서드는 해당 클래스 안에서만 접근 가능</p>
<pre><code class="language-java">public class Student {
    private int studentNo;
    private String name;
}</code></pre>
<hr>
<h3 id="default">default</h3>
<p>접근 제어자를 별도로 설정하지 않으면 변수나 메서드는 default 접근 제어자가 자동으로 설정돼 동일한 패키지 안에서만 접근 가능</p>
<p><em>house/HouseMoon.java</em></p>
<pre><code class="language-java">package house;

public class HouseMoon {
    String lastName = &quot;Moon&quot;; // lastName은 default
}</code></pre>
<p><em>house/HouseYoun.java</em></p>
<pre><code class="language-java">package house;

public class HouseYoun {
    String lastName = &quot;Youn&quot;;

    public static void main(String[] args) {
        HouseMoon moon = new HouseMoon();
        System.out.println(moon.lastName); // HouseMoon 클래스의 lastName 변수 사용 가능
    }
}</code></pre>
<hr>
<h3 id="protected">protected</h3>
<p>접근 제어자가 protected로 설정되어있다면 protected가 붙은 변수나 메서드는 <strong>동일 패키지의 클래스 또는 해당 클래스를 상속받은 클래스</strong>에서만 접근이 가능</p>
<p><em>house/HouseMoon.java</em></p>
<pre><code class="language-java">package house;

public class HouseMoon {
    protected String lastName = &quot;Moon&quot;;
}</code></pre>
<p><em>house/person/ShMon.java</em></p>
<pre><code class="language-java">package house.person; //  패키지 다름

public class ShMon extends HouseMoon { // HouseMoon 상속
    public static void main(String[] args) {
        ShMon shm = new ShMon();
        System.out.println(shm.lastName); // 상속한 클래스의 protected 접근 가능
    }
}</code></pre>
<hr>
<h3 id="public">public</h3>
<p>접근 제어자가 public이면 어떤 클래스에서도 접근이 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[패키지]]></title>
            <link>https://velog.io/@sanghyeok_moon/%ED%8C%A8%ED%82%A4%EC%A7%80</link>
            <guid>https://velog.io/@sanghyeok_moon/%ED%8C%A8%ED%82%A4%EC%A7%80</guid>
            <pubDate>Tue, 18 Mar 2025 12:17:17 GMT</pubDate>
            <description><![CDATA[<p><strong>패키지(package)는 비슷한 성격의 클래스들을 모아 놓은 자바의 디렉터리</strong></p>
<hr>
<h3 id="패키지-알아보기">패키지 알아보기</h3>
<p>house/HouseKim.java</p>
<pre><code class="language-java">package house;

public class HouseKim{
}</code></pre>
<p>house/HousePark.java</p>
<pre><code class="language-java">package house;

public class HousePark{
}</code></pre>
<p><strong>package</strong>는 이 파일이 어떤 패키지의 파일인지를 알려주는 역할</p>
<hr>
<h3 id="서브-패키지란">서브 패키지란?</h3>
<p>house/person/ShMon.java</p>
<pre><code class="language-java">package house.person;

public class ShMon {
}</code></pre>
<p>ShMon 클래스의 package가 <code>house.person</code>으로 생성됨
이렇게 패키지는 도트(<code>.</code>)를 이용하여 서브 패키지를 표시한다.
다시 말해, <code>house.person</code>은 <code>house</code> 패키지의 서브 패키지이다.</p>
<hr>
<h3 id="패키지-사용하기">패키지 사용하기</h3>
<p>다른 클래스에서 ShMon 클래스를 사용하려면 다음과 같이 import 해야함</p>
<pre><code class="language-java">import house.person.ShMon;

public class Sample {
    public static void main(String[] args) {
        ShMon shmon = new ShMon();
    }
}</code></pre>
<p>같은 패키지 내에 있는 클래스는 import 없이 사용 가능</p>
<hr>
<h3 id="패키지를-사용하는-이유">패키지를 사용하는 이유</h3>
<p>패키지를 사용하면 비슷한 클래스끼리 묶어 클래스 분류가 용이함
패키지명이 다른 경우 클래스명이 동일해도 충돌 없이 사용할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[추상 클래스]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Tue, 18 Mar 2025 12:04:08 GMT</pubDate>
            <description><![CDATA[<p>추상 클래스(abstract class)는 <strong>인터페이스의 역할 + 클래스의 기능</strong>을 가지는 자바의 <strong>돌연변이</strong> 같은 클래스이다.</p>
<hr>
<p>Predator 인터페이스 -&gt; 추상 클래스</p>
<pre><code class="language-java">abstract class Predator extends Animal {
    abstract String getFood();
    void printFood() { // default 제거
        System.out.printf(&quot;my food is %s\n&quot;, getFood());
    }
}</code></pre>
<p>추상 클래스를 만들려면 <strong>class 앞에 abstract</strong>를 표기해야 한다.
또한 인터페이스의 메서드와 같은 역할을 하는 메서드에도 <strong>abstract</strong>를 붙여야 한다.
<strong>abstract 메서드는 인터페이스의 메서드와 마찬가지로 구현체가 없다.</strong>
Animal 클래스의 기능을 유지하기 위해 Animal 클래스를 상속하였다.</p>
<hr>
<p><strong>추상 클래스는 일반 클래스와 달리 단독으로 객체를 생성할 수 없음
반드시 추상 클래스를 상속한 실제 클래스를 통해서만 객체를 생성할 수 있다.</strong></p>
<hr>
<pre><code class="language-java">class Tiger extends Predator implements Barkable {
    (...생략)
}
class Lion extends Predator implemnts Barkable {
    (...생략)
}</code></pre>
<p>추상 클래스에 abstract로 선언된 메서드는 인터페이스의 메서드와 마찬가지로 <strong>반드시 구현 해야</strong>한다. 추상 클래스에는 abstract 메서드 외에 실제 메서드도 사용할 수 있다. 추상 클래스에 실제 메서드를 추가하면 Tiger, Lion 등으로 만들어진 객체에서 그 메서드들을 모두 사용할 수 있게 된다.
즉, 인터페이스의 default 메서드가 추상 클래스의 일반 메서드임</p>
<hr>
<h3 id="인터페이스와-추상-클래스의-차이">인터페이스와 추상 클래스의 차이</h3>
<p>추상 클래스는 인터페이스와 달리 일반 클래스처럼 객체 변수, 생성자, private 메서드 등을 가질 수 있다. </p>
<h4 id="1-목적">1. 목적</h4>
<ul>
<li>추상 클래스: <strong>&quot;IS-A&quot; 관계</strong>를 나타냄,<strong>공통 기능의 상속</strong>과 <strong>기본 구현 제공</strong>에 초점.<br>예) <code>Animal</code> 추상 클래스를 상속 받은 <code>Dog</code>,<code>Cat</code>은 동물 공통 특성을 공유</li>
<li>인터페이스: <strong>&quot;CAN-DO&quot; 관계</strong>를 나타냄, <strong>다양한 객체의 특정 행위 강제</strong>에 초점
예) <code>Flyable</code> 인터페이스를 구현한 <code>Bird</code>, <code>Airplane</code>은 서로 다른 종류지만 &quot;날 수 있음&quot;이라는 기능을 보장</li>
</ul>
<hr>
<h4 id="2-구성-요소">2. 구성 요소</h4>
<table>
<thead>
<tr>
<th>구분</th>
<th>추상 클래스</th>
<th>인터페이스</th>
</tr>
</thead>
<tbody><tr>
<td>추상 메서드</td>
<td>1개 이상 필수</td>
<td>모든 메서드 기본 추상화 (구현 불필요)</td>
</tr>
<tr>
<td>구현 메서드</td>
<td>가능</td>
<td><code>default</code>/<code>static</code> 메서드만 구현 가능</td>
</tr>
<tr>
<td>멤버 변수</td>
<td>인스턴스 변수 가능</td>
<td><code>public static final</code> 상수만 허용</td>
</tr>
<tr>
<td>생성자</td>
<td>존재함</td>
<td>불가능</td>
</tr>
<tr>
<td>초기화 블록</td>
<td>가능</td>
<td>불가능</td>
</tr>
</tbody></table>
<hr>
<h4 id="3-상속구현-방식">3. 상속/구현 방식</h4>
<ul>
<li>추상 클래스: 단일 상속만 지원 (<code>extends</code> 키워드 사용)<pre><code class="language-java">public abstract class Animal { ... }
public class Dog extends Animal { ... } // 단일 상속</code></pre>
</li>
<li>인터페이스: 다중 구현 가능(<code>implements</code> 키워드 사용)<pre><code class="language-java">public interface Swimmable { ... }
public interface Barkable { ... }
public class Duck implements Swimmable, Barkable { ... } // 다중 구현</code></pre>
</li>
</ul>
<hr>
<h4 id="4-접근-제어자">4. 접근 제어자</h4>
<ul>
<li>추상 클래스: <code>public</code>, <code>protected</code>, <code>default</code> 등 다양한 접근 제어자 사용 가능</li>
<li>인터페이스: 모든 메서드는 <code>public</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[다형성]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EB%8B%A4%ED%98%95%EC%84%B1</link>
            <guid>https://velog.io/@sanghyeok_moon/%EB%8B%A4%ED%98%95%EC%84%B1</guid>
            <pubDate>Tue, 18 Mar 2025 08:56:23 GMT</pubDate>
            <description><![CDATA[<p><strong>다형성(polymorphism)은 객체 지향 프로그래밍의 특징 중 하나</strong>이다.</p>
<pre><code class="language-java">interface Predator {
    String getFood();
}

interface Barkable {
    void bark();
}

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Tiger extends Animal implements Predator, Barkable{
    @Override
    public String getFood() {
        return &quot;apple&quot;;
    }

    @Override
    public void bark() {
        System.out.println(&quot;어흥&quot;);
    }
}

class Lion extends Animal implements Predator, Barkable{
    @Override
    public String getFood() {
        return &quot;banana&quot;;
    }

    @Override
    public void bark() {
        System.out.println(&quot;으르렁&quot;);
    }
}

class ZooKeeper {
    void feed(Predator predator) {
        System.out.println(&quot;feed&quot; + predator.getFood());
    }
}

// 주목!!
class Bouncer {
    void barkAnimal (Barkable animal) { // Animal 대신 Barkable 사용
        animal.bark();
    }
}

public class Sample {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ZooKeeper zk = new ZooKeeper();
        Tiger tiger = new Tiger();
        Lion lion = new Lion();
        zk.feed(tiger);
        zk.feed(lion);
    }

}</code></pre>
<p>Bouncer 클래스의 barkAnimal 메서드에 주목하자.
barkAnimal 메서드의 입력 자료형이 Animal에서 Barkable로 변경되었다.
그리고 bark 메서드를 호출하도록 코드를 구성하였다.</p>
<p>위 코드에서 tiger, lion 객체는 각각 Tiger, Lion 클래스의 객체이면서 Animal 클래스의 객체이기도 하고, Barkable과 Predator 인터페이스의 객체이기도 하다.
이러한 이유로 barkAnimal 메서드의 입력 자료형을 Animal에서 Barkable로 바꾸어 사용할 수 있는 것이다.</p>
<p>이렇게 <strong>하나의 객체가 여러 개의 자료형 타입을 가질 수 있는 것</strong>을 객체 지향 세계에서는 <strong>다형성</strong>이라 한다.</p>
<hr>
<p>그러므로 Tiger 클래스의 객체는 여러 가지 자료형으로 표현 가능하다.</p>
<pre><code class="language-java">Tiger tiger = new Tiger();  // Tiger is a Tiger
Animal animal = new Tiger();  // Tiger is a Animal
Predator predator = new Tiger();  // Tiger is a Predator
Barkable barkable = new Tiger();  // Tiger is a Barkable</code></pre>
<p>여기서 중요한 점은 Predator로 선언된 predator 객체와 Barkable로 선언된 barkable 객체는 사용할 수 있는 메서드가 서로 다르다.
predator 객체는 getFood() 메서드만 호출이 가능하고 마찬가지로 barkable 객체는 bark() 메서드만 호출이 가능하다.</p>
<hr>
<p>만약 getFood() 메서드와 bark() 메서드를 모두 사용하고 싶다면 어떻게 할까?</p>
<ol>
<li>Predator, Barkable 인터페이스를 구현한 Tiger로 선언된 tigert 객체를 사용한다.</li>
<li>getFood, bark 메서드를 모두 포함하는 새로운 인터페이스를 만들어 사용한다.</li>
</ol>
<pre><code class="language-java">interface Predator {
    String getFood();
}

interface Barkable {
    void bark();
}

interface BarkablePredator extends Predator, Barkable {}</code></pre>
<p>코드를 보면 알겠지만 인터페이스는 일반 클래스와 달리 extends를 이용하여 여러 개의 인터페이스를 동시에 상속할 수 있다.
즉 <strong>인터페이스는 다중 상속</strong>이 지원된다.</p>
<hr>
<p>인터페이스 다중상속 코드 적용 예시</p>
<pre><code class="language-java">class Lion extends Animal implements BarkablePredator {
    public String getFood() {
        return &quot;banana&quot;;
    }

    public void bark() {
        System.out.println(&quot;으르렁&quot;);
    }
}</code></pre>
<p><strong>자식 인터페이스로 생성한 객체의 자료형은 부모 인터페이스로 사용하는 것이 가능하다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인터페이스]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Tue, 18 Mar 2025 08:29:55 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">interface Predator {
    String getFood();
}

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Tiger extends Animal implements Predator{
    @Override
    public String getFood() {
        return &quot;apple&quot;;
    }
}

class Lion extends Animal implements Predator{
    @Override
    public String getFood() {
        return &quot;banana&quot;;
    }
}

class ZooKeeper {
    void feed(Predator predator) {
        System.out.println(&quot;feed&quot; + predator.getFood());
    }
}

public class Sample {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ZooKeeper zk = new ZooKeeper();
        Tiger tiger = new Tiger();
        Lion lion = new Lion();
        zk.feed(tiger);
        zk.feed(lion);
    }

}</code></pre>
<hr>
<h3 id="인터페이스가-왜-필요할까">인터페이스가 왜 필요할까?</h3>
<p>동물 클래스의 종류만큼 feed 메서드가 필요했던 ZooKeeper 클래스를 Predator 인터페이스를 이용하여 구현했더니 단 한 개의 feed 메서드로 구현이 가능해졌다.
여기서 핵심은 메서드가 줄어든 것이 아닌 <strong>ZooKeeper 클래스가 동물 클래스에 의존적인 클래스에서 동물 클래스와 상관없는 독립적인 클래스가 되었다는 점이다.</strong>
이것이 인터페이스의 핵심이다.</p>
<hr>
<h3 id="상속-vs-인터페이스">상속 vs 인터페이스</h3>
<p>Predator 인터페이스 대신 Animal 클래스에서 getFood 메서드를 추가하고 오버라이딩 해서 사용 가능.
하지만 상속은 자식 클래스가 부모 클래스의 메서드를 오버라이딩 하지 않고 사용 가능하기에 해당 메서드를 반드시 구현해야 한다는 <strong>강제성</strong> 을 가지 못한다.
여기서 기억해야 할 점은 <strong>인터페이스는 인터페이스의 메서드를 반드시 구현해야 하는 강제성을 가짐</strong> </p>
<hr>
<h3 id="디폴트-메서드">디폴트 메서드</h3>
<p>자바 8 버전 이후로 디폴트 메서드 (default method)를 사용할 수 있다.
인터페이스의 메서드는 구현체를 가질 수 없지만 디폴트 메서드를 사용하면 실제 구현된 형태의 메서드를 가질 수 있다.</p>
<pre><code class="language-java">interface Predator {
    String getFood();

    default void printFood() {
        System.out.printf(&quot;my food is %s\n&quot;, getFood());
    }
}</code></pre>
<p>디폴트 메서드는 <strong>메서드명 가장 앞에 default</strong>라고 표기해야 함.
인터페이스에 디폴트 메서드를 구현하면 인터페이스를 구현한 실제 클래스는 printFood() 메서드를 구현하지 않아도 사용 가능함.
디폴트 메서드 또한 오버라이딩 가능함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성자]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%83%9D%EC%84%B1%EC%9E%90-constructor</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%83%9D%EC%84%B1%EC%9E%90-constructor</guid>
            <pubDate>Tue, 18 Mar 2025 07:58:19 GMT</pubDate>
            <description><![CDATA[<h3 id="생성자란">생성자란?</h3>
<p><strong>메서드명이 클래스명과 동일</strong>하고 <strong>리턴 자료형을 정의하지 않는</strong> 메서드</p>
<pre><code class="language-java">class HouseDog extends Dog {
    // 생성자 코드 에시
    HouseDog (String name) {
        this.setName(name);
    }
}</code></pre>
<hr>
<h3 id="생성자-규칙">생성자 규칙</h3>
<ul>
<li>클래스명과 메서드명이 같다.</li>
<li>리턴 타입을 정의하지 않는다. (void도 사용하지 않는다.)</li>
</ul>
<hr>
<h3 id="디폴트-생성자">디폴트 생성자</h3>
<pre><code class="language-java">class Dog extends Animal {
    // 디폴트 생성자
    Dog() {
    }

    void sleep() {
        System.out.println(this.name + &quot; zzz&quot;);
    }
}
</code></pre>
<p><strong>생성자의 입력 항목이 없고 생성자 내부에 아무 내용이 없는</strong> 생성자를 디폴트 생성자라고 부른다.
만약 클래스에 생성자가 하나도 없다면 <strong>컴파일러는 자동으로 이와 같은 디폴트 생성자를 추가</strong> 한다.
하지만 사용자가 작성한 생성자가 하나라도 구현되어 있다면 컴파일러는 디폴트 생성자를 추가하지 않는다.</p>
<hr>
<h3 id="생성자-오버로딩">생성자 오버로딩</h3>
<pre><code class="language-java">class HouseDog extends Dog {
    HouseDog(String name) {
        this.setName(name);
    }

    HouseDog() {}

    HouseDog(int type) {
        if (type == 1) {
            this.setName(&quot;bulldog&quot;);
        } else {
            this.setName(&quot;jindo&quot;);
        }
    }
}</code></pre>
<p>입력 항목이 다른 생성자를 여러 개 만들 수 있는데 이러한 방식을 <strong>생성자 오버로딩 (constructor overloading)</strong> 이라 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오버라이딩과 오버로딩]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EA%B3%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EA%B3%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9</guid>
            <pubDate>Tue, 18 Mar 2025 07:43:47 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">class Animal {
    Stirng name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name + &quot; zzz&quot;);
    }
}

class HouseDog extends Dog {
    // 메서드 오버라이딩
    @Override
    void sleep() {
        System.out.println(this.name + &quot; zzz in house&quot;);
    }
    // 메서드 오버로딩
    void sleep(int hour) {
        System.out.println(this.name + &quot; zzz in house for &quot; + hour + &quot; hours&quot;);
    }
}
</code></pre>
<p>HouseDog 클래스에 주목하자</p>
<p>첫 번째 메서드:
Dog 클래스에 있는 sleep 메서드를 HouseDog 클래스에 다시 구현하였다.
이렇게 부모 클래스의 메서드를 자식 클래스가 동일한 형태(즉, 입출력이 동일)로 또다시 구현하는 행위를 <strong>메서드 오버라이딩 (method overriding, 메서드 덮어쓰기)</strong> 라고 한다.</p>
<p>두 번째 메서드:
이미 sleep이라는 메서드가 있지만 동일한 이름의 sleep 메서드를 또 생성할 수 있다.
단, 메서드의 입력 항목이 다를 경우만 가능
이렇듯 입력 항목이 다른 경우 동일한 이름의 메서드를 만들 수 있는데 이를 <strong>메서드 오버로딩 (method overloading)</strong> 이라 부른다.</p>
<hr>
<h3 id="참고">참고</h3>
<p><strong>자바는 다중 상속을 지원하지 않는다</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IS-A 관계란?]]></title>
            <link>https://velog.io/@sanghyeok_moon/IS-A-%EA%B4%80%EA%B3%84%EB%9E%80</link>
            <guid>https://velog.io/@sanghyeok_moon/IS-A-%EA%B4%80%EA%B3%84%EB%9E%80</guid>
            <pubDate>Tue, 18 Mar 2025 07:20:17 GMT</pubDate>
            <description><![CDATA[<p>Dog 클래스는 Animal 클래스를 상속함
즉, Dog는 Animal의 하위 개념이라 할 수 있다.
이런 경우 Dog는 Animal에 포함되기 때문에 &#39;개(Dog)는 동물(Animal)이다&#39;라고 표현 가능
자바는 이러한 관계를 IS-A 관계라고 한다.</p>
<pre><code class="language-java">Animal dog = new Dog(); // Dog is a Animal</code></pre>
<ul>
<li>주의점: Dog 객체를 Animal 자료형으로 사용할 경우 Dog 클래스에만 존재하는 메서드는 사용 불가</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[매개 변수와 인수]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EB%A7%A4%EA%B0%9C-%EB%B3%80%EC%88%98%EC%99%80-%EC%9D%B8%EC%88%98</link>
            <guid>https://velog.io/@sanghyeok_moon/%EB%A7%A4%EA%B0%9C-%EB%B3%80%EC%88%98%EC%99%80-%EC%9D%B8%EC%88%98</guid>
            <pubDate>Tue, 18 Mar 2025 07:03:55 GMT</pubDate>
            <description><![CDATA[<p>매개 변수(parameter)와 인수(arguments)는 혼용되는 헷갈리는 용어.
매개 변수는 메서드에 전달된 입력값을 저장하는 변수를 의미
인수는 메서드를 호출할 때 전달하는 입력값을 의미</p>
<pre><code class="language-java">public class Sample {
    int sum (int x, int y) { // x, y는 매개 변수(parameter)
        return x + y;
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        int z = sample.sum(5, 10); // 5, 10는 인수 (arguments)
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git 복원]]></title>
            <link>https://velog.io/@sanghyeok_moon/Git-%EB%B3%B5%EC%9B%90</link>
            <guid>https://velog.io/@sanghyeok_moon/Git-%EB%B3%B5%EC%9B%90</guid>
            <pubDate>Tue, 18 Mar 2025 06:34:32 GMT</pubDate>
            <description><![CDATA[<p>Git에서는 커밋된 내용을 수정하거나 되돌릴 수 있는 여러 방법이 있다.
이 과정에서 많이 사용되는 명령어는 <code>git restore</code>, <code>git revert</code>, <code>git reset</code>이다.</p>
<h3 id="git-restore">git restore:</h3>
<ul>
<li>스테이징된 변경 사항이나 작업 디렉터리의 변경 사항을 되돌릴 때 사용</li>
<li><code>git restore &lt;파일명&gt;</code>을 사용하여 특정 파일의 변경 사항을 취소할 수 있다</li>
<li><code>git restore --staged &lt;파일명&gt;</code>을 사용하면 스테이징된 변경 사항만 복원</li>
</ul>
<h3 id="git-revert">git revert:</h3>
<ul>
<li>이전 커밋을 되돌리면서 새로운 커밋을 생성</li>
<li>기존 커밋을 그대로 유지하면서 되돌리는 방법, 프로젝트의 이력을 보존하면서 수정할 수 있다<pre><code>$ git revert &lt;커밋 해시&gt;</code></pre></li>
</ul>
<h3 id="git-reset">git reset:</h3>
<ul>
<li>특정 커밋으로 되돌아가면서, 그 이후의 변경 사항을 완전히 제거한다.</li>
<li><code>git reset --hard</code> 명령어를 사용하면 작업 디렉터리와 스테이징 영역의 모든 변경 사항이 삭제된다.<pre><code>$ git reset --hard &lt;커밋 해시&gt;</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[이미 존재하는 프로젝트를 Git으로 관리하는 법]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%9D%B4%EB%AF%B8-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-Git%EC%9C%BC%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%9D%B4%EB%AF%B8-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-Git%EC%9C%BC%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Tue, 18 Mar 2025 05:39:12 GMT</pubDate>
            <description><![CDATA[<p><code>git init</code> 의 작동:</p>
<p><code>git init</code> 을 실행하면 해당 디렉터리(그리고 하위 디렉터리)에서 Git 리퍼지터리가 생성</p>
<p><code>.git</code> 이라는 숨김 폴더가 생성되며, 이 폴더에 Git이 사용하는 메타데이터와 설정 정보가 저장됨</p>
<h3 id="이미-파일이-있는-디렉터리에서-실행했을-때">이미 파일이 있는 디렉터리에서 실행했을 때:</h3>
<p>디렉터리에 있는 기존 파일들은 Git의 추적 대상에서 제외.</p>
<p>즉, 바로 버전관리되지 않는다.</p>
<p><code>git status</code> 를 실행하면 해당 디렉터리에 있는 파일들이 &quot;Untracked files&quot;로 표시</p>
<h3 id="다음-단계--git으로-파일-추적-시작">다음 단계 : Git으로 파일 추적 시작:</h3>
<ul>
<li><p>추적할 파일 추가</p>
<ul>
<li><p>현재 디렉터리의 모든 파일과 폴더를 Git의 추적 대상으로 추가</p>
</li>
<li><p>특정 파일만 추가하려면 파일명을 명시: <code>git add 파일명</code></p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-git">git add .</code></pre>
<ul>
<li><p>커밋 생성</p>
<ul>
<li>현재 추가된 모든 파일의 상태를 기록하며, &quot;Initial commit&quot; 이라는 메시지를 남긴다 </li>
</ul>
</li>
</ul>
<pre><code class="language-git">git commit -m &quot;Initial commit&quot;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[.gitignore란?]]></title>
            <link>https://velog.io/@sanghyeok_moon/.gitignore%EB%9E%80</link>
            <guid>https://velog.io/@sanghyeok_moon/.gitignore%EB%9E%80</guid>
            <pubDate>Tue, 18 Mar 2025 05:14:10 GMT</pubDate>
            <description><![CDATA[<p><code>.gitignore</code> 파일은 Git이 특정 파일을 추적하지 않도록 설정하는 파일이다.
일반적으로 프로젝트 루트(홈)에 위치하며, 프로젝트마다 별도로 설정할 수 있다.
Git은 <code>.gitignore</code> 파일의 내용을 기준으로 특정 파일을 무시하며, 이는 프로젝트 전체에 적용된다.</p>
<h3 id="gitignore-파일-작성-방법">.gitignore 파일 작성 방법</h3>
<p><code>.gitignore</code> 파일은 프로젝트 루트 디렉터리에 생성하며, 무시할 파일 또는 디렉터리의 패턴을 한 줄씩 작성한다.</p>
<pre><code class="language-gitignore"># 로그 파일 제외
logs/
*.log

# 빌드 결과물 제외
/build/
*.o
*.class

# 환경 설정 파일 제외
.env
config/local.json</code></pre>
<h3 id="자주-사용하는-gitignore-패턴">자주 사용하는 .gitignore 패턴</h3>
<ul>
<li>특정 파일 확장자 제외</li>
</ul>
<pre><code class="language-gitignore"># 모든 .log 파일 제외
*.log
# 모든 .tmp 파일 제외
*.tmp</code></pre>
<ul>
<li>특정 디렉터리 제외</li>
</ul>
<pre><code class="language-gitignore"># npm 패키지 디렉터리 제외
node_modules/
# Python 가상 환경 제외
venv/</code></pre>
<ul>
<li>특정 파일 포함 예외</li>
</ul>
<pre><code class="language-gitignore"># important.log 파일은 추적
*.log
!important.log</code></pre>
<h3 id="gitignore가-적용되지-않는-경우">.gitignore가 적용되지 않는 경우</h3>
<p>이미 Git에 추가된 파일은 <code>.gitignore</code> 에 추가해도 무시되지 않는다.</p>
<p>이 경우, 아래 명령어로 해당 파일을 추적 목록에서 제거한 후 커밋해야 한다.</p>
<pre><code class="language-gitignore">git rm --cached &lt;파일명&gt;
git commit -m &quot;Remove tracked file&quot;</code></pre>
<h3 id="gitignore-파일-적용-여부-확인">.gitignore 파일 적용 여부 확인</h3>
<p><code>.gitignore</code> 가 제대로 작동하는지 확인하는 명령어</p>
<pre><code class="language-gitignore">git check-ignore -v &lt;파일명&gt;</code></pre>
<h3 id="글로벌-gitignore-설정">글로벌 .gitignore 설정</h3>
<p>모든 Git 프로젝트에 공통적으로 적용할 무시 패턴을 설정</p>
<pre><code class="language-gitignore">git config --global core.excludesfile ~/.gitignore_global</code></pre>
<p>이 후 <code>~/.gitignore_global</code> 파일을 생성하고 패턴을 추가한다.</p>
<pre><code class="language-gitignore"># macOS 시스템 파일 제외
.DS_Store
# Windows 썸네일 캐시 제외 
Thumbs.db</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이벤트 루프 처리 과정에서의 우선순위]]></title>
            <link>https://velog.io/@sanghyeok_moon/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EC%B2%98%EB%A6%AC-%EA%B3%BC%EC%A0%95%EC%97%90%EC%84%9C%EC%9D%98-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84</link>
            <guid>https://velog.io/@sanghyeok_moon/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EC%B2%98%EB%A6%AC-%EA%B3%BC%EC%A0%95%EC%97%90%EC%84%9C%EC%9D%98-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84</guid>
            <pubDate>Wed, 12 Mar 2025 10:41:58 GMT</pubDate>
            <description><![CDATA[<h3 id="1-콜-스택-call-stack">1. 콜 스택 (Call Stack)</h3>
<p><strong>자바스크립트 엔진이 함수 호출을 관리하는 스택 자료구조</strong></p>
<p>이벤트 루프는 제일 먼저 콜 스택의 작업을 처리한다.</p>
<p>콜 스택에는 현재 실행 중인 동기적인 코드가 쌓이고, 이 작업들이 모두 완료되어 콜 스택이 비워질 때까지 다른 큐로 이동하지 않는다.</p>
<p>즉, 콜 스택이 최우선</p>
<p><strong>콜 스택의 역할</strong></p>
<ul>
<li><p>함수가 호출되면 해당 함수의 <strong>실행 컨텍스트(Execution Context)</strong> 가 콜 스택에 push된다.</p>
</li>
<li><p>스택의 맨 위에 있는 함수가 실행됨</p>
</li>
<li><p>함수 실행 종료시 pop</p>
</li>
</ul>
<p><strong>예제 코드</strong></p>
<pre><code class="language-javascript">function foo() {
    console.log(&#39;foo&#39;);
}

function bar() {
    foo();
    console.log(&#39;bar&#39;);
}

bar();</code></pre>
<p><strong>실행 순서</strong></p>
<ol>
<li><p><code>bar()</code>가 호출되면 콜 스택에 <code>bar</code>가 push됨</p>
</li>
<li><p><code>bar</code> 안에서 <code>foo()</code>가 콜 스택에 push</p>
</li>
<li><p><code>foo</code>가 실행돼 <code>&#39;foo&#39;</code>가 출력되고, 스택에서 pop</p>
</li>
<li><p><code>bar</code>가 <code>&#39;bar&#39;</code>를 출력하고 스택에서 pop</p>
</li>
</ol>
<h3 id="2-마이크로태스크-큐microtask-queue">2. 마이크로태스크 큐(Microtask Queue)</h3>
<p><strong>우선순위가 높은 비동기 작업의 콜백이 대기하는 큐</strong></p>
<p>콜 스택이 비면, 이벤트 루프는 마이크로태스크 큐로 이동한다. (태스크 큐보다 먼저 처리)</p>
<p>여기에는 주로 <code>Promise</code>의 <code>then</code>, <code>catch</code>, <code>finally</code> 핸들러나 <code>MutationObserver</code> 같은 작업이 대기한다.</p>
<p>마이크로태스크 큐는 모든 작업이 처리될 때까지 계속 실행되며, 큐가 완전히 비워질 때까지 다음 단계로 넘어가지 않는다.</p>
<p><strong>마이크로태스크 큐의 역할</strong></p>
<ul>
<li><p>마이크로태스크:<code>Promise</code>의 <code>then</code>,<code>catch</code>,<code>finally</code> 핸들러, <code>MutationObserver</code> 콜백 등</p>
</li>
<li><p>처리 순서: 이벤트 루프는 콜 스택이 비어 있을 때, <strong>마이크로태스크 큐를 먼저 처리하고</strong>, 그 다음 태스크 큐를 처리한다.</p>
</li>
</ul>
<p><strong>예제 코드</strong></p>
<pre><code class="language-javascript">console.log(&#39;Start&#39;);

Promise.resolve().then(() =&gt; {
    console.log(&#39;Promise&#39;);
});

setTimeout(() =&gt; {
    console.log(&#39;Timeout&#39;);
}, 0);

console.log(&#39;End&#39;);</code></pre>
<p><strong>실행 순서</strong></p>
<ol>
<li><p><code>Start</code>가 출력</p>
</li>
<li><p><code>Promise.resolve()</code>가 호출되고, <code>then</code> 핸들러가 마이크로태스크 큐에 들어감</p>
</li>
<li><p><code>setTimeout</code>이 Web APIs로 넘어가 0초 후에 실행 대기 상태로 전환</p>
</li>
<li><p><code>End</code>출력</p>
</li>
<li><p>콜 스택이 비었으므로, 이벤트 루프가 마이크로태스크 큐를 먼저 처리</p>
<ul>
<li><p>마이크로태스크 큐에서 <code>Promise 콜백</code>이 꺼내져 콜 스택에 푸쉬</p>
</li>
<li><p><code>Promise</code>가 출력된다.</p>
</li>
</ul>
</li>
<li><p>그 후 태스크 큐로 넘어가 <code>Timeout 콜백</code>이 처리된다.</p>
</li>
</ol>
<p><strong>마이크로태스크 큐는 태스크 큐보다 우선순위가 높아, <code>Promise</code>가 <code>setTimeout</code>보다 먼저 실행된다.</strong></p>
<h3 id="3-태스크-큐task-queue">3. 태스크 큐(Task Queue)</h3>
<p><strong>우선 순위가 낮은 비동기 작업의 콜백 함수가 대기하는 큐</strong></p>
<p>마이크로태스크 큐가 비어 있으면, 이벤트 루프는 태스크 큐에서 하나의 작업을 꺼내 콜 스택에 넣고 실행한다.</p>
<p>태스크 큐에는 <code>setTimeout</code>, <code>setInterval</code>, HTTP 요청 등의 비동기 콜백이 대기한다.</p>
<p>태스크 큐는 한 번에 하나의 작업만 처리하고, 작업이 끝나면 다시 콜 스택과 마이크로태스크 큐를 확인한다.</p>
<p><strong>태스크 큐의 역할</strong></p>
<ul>
<li><p>비동기 작업: <code>setTimeout</code>, <code>setInterval</code>, HTTP 요청 등의 작업</p>
</li>
<li><p>콜백 함수: 비동기 작업이 끝난 후 실행될 함수가 태스크 큐에 저장</p>
</li>
<li><p>이벤트 루프가 콜 스택이 비어 있을 때 태스크 큐에서 작업을 꺼내 실행</p>
</li>
</ul>
<p><strong>예제 코드</strong></p>
<pre><code class="language-javascript">console.log(&#39;Start&#39;);

setTimeout(() =&gt; {
    console.log(&#39;Timeout&#39;);
}, 0);

console.log(&#39;End&#39;);</code></pre>
<p><strong>실행 순서</strong> </p>
<ol>
<li><code>&#39;Start&#39;</code>가 출력된다. (콜 스택: <code>[console.log] → []</code>)</li>
<li><code>setTimeout</code>이 호출되지만, 즉시 실행되지 않고 <strong>Web APIs</strong>로 넘어가 0초 후에 실행 대기 상태가 된다.<ul>
<li><code>콜 스택: []</code></li>
</ul>
</li>
<li><code>&#39;End&#39;</code>가 출력된다. (콜 스택: <code>[console.log] → []</code>)</li>
<li>타이머(0초)가 만료되면 콜백 함수 <code>() =&gt; console.log(&#39;Timeout&#39;)</code>가 태스크 큐에 들어간다.<ul>
<li>태스크 큐: <code>[Timeout 콜백]</code></li>
</ul>
</li>
<li>이벤트 루프가 콜 스택이 비어 있는 것을 확인하고, 태스크 큐에서 콜백을 꺼내 콜 스택에 push.<ul>
<li>콜 스택: <code>[Timeout 콜백]</code></li>
</ul>
</li>
<li><code>&#39;Timeout&#39;</code>이 출력되고 콜백이 제거된다.<ul>
<li>콜 스택: <code>[]</code></li>
</ul>
</li>
</ol>
<p>**참고: <code>setTimeout</code>의 시간이 0초라도 바로 실행되지 않는다. Web APIs에서 처리된 후 태스크 큐로 이동하기 때문</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 람다식]]></title>
            <link>https://velog.io/@sanghyeok_moon/Java-%EB%9E%8C%EB%8B%A4%EC%8B%9D</link>
            <guid>https://velog.io/@sanghyeok_moon/Java-%EB%9E%8C%EB%8B%A4%EC%8B%9D</guid>
            <pubDate>Tue, 11 Mar 2025 13:56:36 GMT</pubDate>
            <description><![CDATA[<p><strong>목차</strong></p>
<ol>
<li><strong>람다식 정의와 핵심 개념</strong></li>
<li><strong>람다식은 왜 사용할까? (사용 이유와 장점)</strong></li>
<li><strong>람다식 문법 완벽 분석</strong><ul>
<li>람다 파라미터 (Parameter)</li>
<li>화살표 토큰 (Arrow Token <code>-&gt;</code>)</li>
<li>람다 몸체 (Body)<ul>
<li>표현식 (Expression Body)</li>
<li>블록 (Block Body)</li>
</ul>
</li>
</ul>
</li>
<li><strong>함수형 인터페이스 (Functional Interface), 람다식의 핵심 퍼즐 조각</strong><ul>
<li>함수형 인터페이스란 무엇일까?</li>
<li><code>@FunctionalInterface</code> 어노테이션</li>
<li>자바에서 제공하는 주요 함수형 인터페이스 (예: <code>Runnable</code>, <code>Comparator</code>, <code>Predicate</code>, <code>Function</code>, <code>Consumer</code>, <code>Supplier</code>)</li>
</ul>
</li>
<li><strong>람다식 활용 예제</strong><ul>
<li>익명 내부 클래스 대체 (익명 함수처럼 간결하게!)<ul>
<li>쓰레드 (Thread) 생성</li>
<li>이벤트 핸들러 (Event Handler) 구현</li>
</ul>
</li>
<li>컬렉션 (Collection) 처리 간결화 (Stream API와 함께 더욱 강력하게!)<ul>
<li><code>forEach</code> (각 요소 순회)</li>
<li><code>map</code> (요소 변환)</li>
<li><code>filter</code> (요소 필터링)</li>
<li><code>reduce</code> (요소 집계)</li>
<li><code>sort</code> (정렬)</li>
</ul>
</li>
</ul>
</li>
<li><strong>람다식의 강력한 장점 (코드 간결성, 가독성, 생산성 향상)</strong><ul>
<li>코드 간결성 및 가독성 향상</li>
<li>함수형 프로그래밍 패러다임 지원</li>
<li>병렬 처리 효율 증대 (Stream API와 함께)</li>
</ul>
</li>
<li><strong>람다식 사용 시 주의사항 (제약 사항 및 한계점)</strong></li>
<li><strong>람다식 vs 익명 내부 클래스 (차이점 명확 비교)</strong></li>
<li><strong>람다식, 언제 어디에 사용해야 할까요? (활용 가이드라인)</strong></li>
<li><strong>마무리</strong></li>
</ol>
<hr>
<h3 id="1-람다식-정의와-핵심-개념">1. 람다식 정의와 핵심 개념</h3>
<p><strong>람다식 (Lambda Expression)</strong> 은 <strong>&quot;익명 함수 (Anonymous Function)&quot;</strong> 를 간결하게 표현하는 방법이다. <strong>익명 함수</strong>란 이름 없이 정의되는 함수를 의미하며, 람다식을 사용하면 클래스 선언 없이 <strong>함수 자체를 값처럼</strong> 사용할 수 있다.</p>
<p><strong>핵심 개념:</strong></p>
<ul>
<li><strong>익명 함수 (Anonymous Function):</strong> 이름이 없는 함수, 클래스 없이 함수 자체를 표현</li>
<li><strong>함수형 프로그래밍 (Functional Programming):</strong> 함수를 값처럼 다루는 프로그래밍 패러다임</li>
<li><strong>코드 간결성:</strong> 불필요한 코드 (클래스 선언, 메소드 이름 등) 를 줄여 코드 라인 수 감소</li>
<li><strong>유연성:</strong> 함수를 변수에 할당, 메소드 인자로 전달, 메소드 반환 값으로 사용 가능</li>
</ul>
<p><strong>기존 방식 (클래스와 메소드):</strong></p>
<pre><code class="language-java">// 1. 요리책 (클래스) 만들기
class 요리책 {
    // 2. 레시피 (메소드) 만들기
    public void 라면레시피() {
        // ... 복잡한 라면 레시피 ...
    }
}

// 3. 요리책 보고 요리하기
요리책 myCookBook = new 요리책();
myCookBook.라면레시피();</code></pre>
<p><strong>람다식 (익명 함수):</strong></p>
<pre><code class="language-java">// 1. 레시피 (람다식) 만들기 (요리책 없이 레시피만!)
() -&gt; { // 람다식 시작
    // ... 간단한 라면 레시피 ...
} // 람다식 끝

// 2. 레시피 (람다식) 사용하기 (레시피 자체를 값처럼!)
요리사.요리하기( () -&gt; { /* 간단한 라면 레시피 */ } ); // 요리사에게 람다식 레시피 전달</code></pre>
<p>기존 방식은 요리책(<code>클래스</code>)을 먼저 만들고, 그 안에 레시피(<code>메소드</code>)를 넣어야 했다. 하지만 람다식은 요리책 없이 <strong>레시피 자체</strong>만을 간단하게 만들어서 <strong>요리사에게 바로 전달</strong>할 수 있는 것처럼, 함수 자체를 간결하게 표현하고 값처럼 사용할 수 있게 해준다.</p>
<h3 id="2-람다식은-왜-사용할까-사용-이유와-장점">2. 람다식은 왜 사용할까? (사용 이유와 장점)</h3>
<p>람다식을 사용하는 주된 이유는 다음과 같다.</p>
<ul>
<li><strong>코드 간결성 향상:</strong> 불필요한 코드를 줄여 코드 라인 수를 줄이고, 코드를 더욱 간결하게 만든다. 특히 짧은 함수 로직을 표현할 때 람다식의 장점이 두드러진다.</li>
<li><strong>가독성 향상:</strong> 코드가 간결해지면서 코드의 의도를 명확하게 드러내고, 코드 가독성을 높여준다.</li>
<li><strong>유지보수성 향상:</strong> 코드가 간결해지면 코드 변경 및 유지보수가 더욱 쉬워진다.</li>
<li><strong>함수형 프로그래밍 지원:</strong> Java를 함수형 프로그래밍 방식으로 코딩할 수 있도록 지원하여, 컬렉션 처리, 병렬 처리 등 다양한 분야에서 효율적인 코드 작성을 가능하게 한다.</li>
<li><strong>Stream API 활용 극대화:</strong> Java 8에서 함께 도입된 Stream API 와 람다식을 함께 사용하면 컬렉션 데이터를 더욱 효율적이고 간결하게 처리할 수 있다.</li>
</ul>
<p><strong>람다식의 핵심 가치:</strong> <strong>&quot;코드를 더 짧고, 더 읽기 쉽고, 더 유연하게 만들어준다!&quot;</strong></p>
<h3 id="3-람다식-문법-완벽-분석-작성-방법-상세-가이드">3. 람다식 문법 완벽 분석 (작성 방법 상세 가이드)</h3>
<p>람다식은 다음과 같은 기본 문법 구조를 가진다.</p>
<pre><code class="language-java">(파라미터) -&gt; { 람다 몸체 }</code></pre>
<p>람다식은 크게 세 부분으로 구성된다.</p>
<ol>
<li><strong>람다 파라미터 (Parameter):</strong> 메소드의 매개변수와 유사. 람다식으로 전달될 입력 값을 정의한다.</li>
<li><strong>화살표 토큰 (Arrow Token <code>-&gt;</code>):</strong> 람다 파라미터와 람다 몸체를 구분하는 화살표 기호. 
&quot;~을 받아서 (파라미터) ~을 실행한다 (람다 몸체)&quot; 라는 의미를 가진다.</li>
<li><strong>람다 몸체 (Body):</strong> 실제로 실행될 코드 블록. 람다 파라미터를 이용하여 특정 연산을 수행하고 결과를 반환하거나, 특정 동작을 실행한다.</li>
</ol>
<p>각 구성 요소를 좀 더 자세히 알아보자</p>
<h4 id="31-람다-파라미터-parameter">3.1. 람다 파라미터 (Parameter)</h4>
<p>람다 파라미터는 람다식으로 전달될 입력 값을 정의한다. 메소드의 매개변수 선언과 유사하지만, 람다식에서는 몇 가지 문법적 특징이 있다.</p>
<ul>
<li><strong>소괄호 <code>()</code> 로 묶어서 선언:</strong> 파라미터 목록은 항상 소괄호 <code>()</code> 로 묶어서 선언한다.</li>
<li><strong>타입 생략 가능 (타입 추론):</strong> 람다 파라미터 타입을 명시적으로 선언하지 않아도 컴파일러가 <strong>타입 추론 (Type Inference)</strong> 을 통해 자동으로 타입을 판단한다. 타입을 생략하면 코드를 더 간결하게 만들 수 있다.</li>
<li><strong>파라미터가 하나인 경우 소괄호 생략 가능:</strong> 람다 파라미터가 <strong>하나</strong>인 경우에는 소괄호 <code>()</code> 를 생략할 수 있다. 하지만 파라미터가 없거나, 여러 개인 경우에는 소괄호 <code>()</code> 를 반드시 사용해야 한다.</li>
<li><strong><code>final</code> 키워드 자동 적용 (effectively final):</strong> 람다 파라미터는 암묵적으로 <code>final</code> 로 선언된 것처럼 동작한다. 람다식 내부에서 파라미터 값을 변경하려고 하면 컴파일 에러가 발생한다. (람다 캡처링과 관련)</li>
</ul>
<p><strong>람다 파라미터 선언 예시:</strong></p>
<table>
<thead>
<tr>
<th>형태</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>()</code></td>
<td>파라미터 없음</td>
<td><code>() -&gt; { ... }</code></td>
</tr>
<tr>
<td><code>(parameter)</code></td>
<td>파라미터 1개, 타입 생략 가능</td>
<td><code>(name) -&gt; { ... }</code>, <code>name -&gt; { ... }</code></td>
</tr>
<tr>
<td><code>(type parameter)</code></td>
<td>파라미터 1개, 타입 명시</td>
<td><code>(String name) -&gt; { ... }</code></td>
</tr>
<tr>
<td><code>(param1, param2, ...)</code></td>
<td>파라미터 여러 개, 타입 생략 가능, 소괄호 필수</td>
<td><code>(name, age) -&gt; { ... }</code></td>
</tr>
<tr>
<td><code>(type param1, ...)</code></td>
<td>파라미터 여러 개, 타입 명시, 소괄호 필수</td>
<td><code>(String name, int age) -&gt; { ... }</code></td>
</tr>
</tbody></table>
<h4 id="32-화살표-토큰-arrow-token--">3.2. 화살표 토큰 (Arrow Token <code>-&gt;</code>)</h4>
<p>화살표 토큰 <code>-&gt;</code> 은 람다 파라미터와 람다 몸체를 구분하는 핵심적인 기호이다. 람다식에서 <strong>&quot;~을 받아서 (파라미터) ~을 실행한다 (람다 몸체)&quot;</strong> 라는 의미를 명확하게 나타낸다. 화살표 토큰은 람다식 문법에서 <strong>반드시</strong> 사용해야 한다.</p>
<h4 id="33-람다-몸체-body">3.3. 람다 몸체 (Body)</h4>
<p>람다 몸체는 람다식의 핵심 로직이 구현되는 부분이다. 람다 파라미터를 이용하여 특정 연산을 수행하고 결과를 반환하거나, 특정 동작을 실행한다. 람다 몸체는 크게 <strong>표현식 (Expression Body)</strong> 과 <strong>블록 (Block Body)</strong> 두 가지 형태를 가질 수 있다.</p>
<h5 id="331-표현식-expression-body">3.3.1. 표현식 (Expression Body)</h5>
<p>표현식 람다 몸체는 <strong>단일한 실행문</strong>으로 구성된 경우에 사용된다. 실행문이 <strong>return 문</strong>인 경우, <strong><code>return</code> 키워드와 <code>{}</code> 중괄호를 생략</strong>하고 표현식만 작성할 수 있다. 표현식 람다 몸체는 코드를 더욱 간결하게 만들어준다.</p>
<p><strong>표현식 람다 몸체 문법:</strong></p>
<pre><code class="language-java">(파라미터) -&gt; 표현식 // return 키워드, {} 중괄호 생략</code></pre>
<p><strong>표현식 람다 몸체 예시:</strong></p>
<pre><code class="language-java">// 1. 덧셈 람다식 (표현식 몸체)
(a, b) -&gt; a + b // return 키워드, {} 중괄호 생략

// 2. 문자열 길이 반환 람다식 (표현식 몸체)
name -&gt; name.length() // return 키워드, {} 중괄호 생략

// 3. 숫자 짝수 판별 람다식 (표현식 몸체)
num -&gt; num % 2 == 0 // return 키워드, {} 중괄호 생략</code></pre>
<h5 id="332-블록-block-body">3.3.2. 블록 (Block Body)</h5>
<p>블록 람다 몸체는 <strong>여러 개의 실행문</strong> 또는 <strong>복잡한 로직</strong>을 포함하는 경우에 사용된다. 실행문들을 <code>{}</code> 중괄호로 묶어서 블록 형태로 작성하며, <strong><code>return</code> 문을 사용하여 명시적으로 반환값을 지정</strong>해야 한다. 블록 람다 몸체는 좀 더 복잡한 함수 로직을 표현할 수 있도록 유연성을 제공한다.</p>
<p><strong>블록 람다 몸체 문법:</strong></p>
<pre><code class="language-java">(파라미터) -&gt; { // {} 중괄호 블록 시작
    // 실행문 1;
    // 실행문 2;
    // ...
    return 반환값; // return 키워드 명시
} // {} 중괄호 블록 끝</code></pre>
<p><strong>블록 람다 몸체 예시:</strong></p>
<pre><code class="language-java">// 1. 덧셈 후 로그 출력 람다식 (블록 몸체)
(a, b) -&gt; {
    System.out.println(&quot;덧셈 연산 시작&quot;);
    int sum = a + b;
    System.out.println(&quot;덧셈 결과: &quot; + sum);
    return sum; // return 키워드 명시
}

// 2. 문자열 검증 및 길이 반환 람다식 (블록 몸체)
name -&gt; {
    if (name == null || name.isEmpty()) {
        System.out.println(&quot;이름이 유효하지 않습니다.&quot;);
        return 0; // return 키워드 명시
    }
    System.out.println(&quot;이름 길이 계산&quot;);
    return name.length(); // return 키워드 명시
}

// 3. 숫자 짝수 판별 및 메시지 출력 람다식 (블록 몸체)
num -&gt; {
    boolean isEven = num % 2 == 0;
    if (isEven) {
        System.out.println(num + &quot;은 짝수입니다.&quot;);
    } else {
        System.out.println(num + &quot;은 홀수입니다.&quot;);
    }
    return isEven; // return 키워드 명시
}</code></pre>
<p><strong>람다 몸체 선택 가이드:</strong></p>
<ul>
<li><strong>단일 실행문 (return 문):</strong> 표현식 람다 몸체 (더 간결)</li>
<li><strong>여러 실행문 또는 복잡한 로직:</strong> 블록 람다 몸체 (유연성 확보)</li>
</ul>
<h3 id="4-함수형-인터페이스-functional-interface-람다식의-핵심-퍼즐-조각">4. 함수형 인터페이스 (Functional Interface), 람다식의 핵심 퍼즐 조각</h3>
<p>람다식은 <strong>함수형 인터페이스 (Functional Interface)</strong> 라는 특별한 인터페이스와 함께 사용될 때 진정한 힘을 발휘한다. 함수형 인터페이스는 람다식을 &quot;담는 그릇&quot; 역할을 하며, 람다식이 어떤 형태로 사용될지를 정의힌다.</p>
<h4 id="41-함수형-인터페이스란-무엇일까">4.1. 함수형 인터페이스란 무엇일까?</h4>
<p><strong>함수형 인터페이스 (Functional Interface)</strong> 는 <strong>&quot;추상 메소드 (Abstract Method) 를 딱 하나만 가지고 있는 인터페이스&quot;</strong> 를 의미한다. 인터페이스는 원래 여러 개의 추상 메소드를 가질 수 있지만, 함수형 인터페이스는 <strong>오직 하나의 추상 메소드</strong>만 허용된다.</p>
<p><strong>함수형 인터페이스 조건:</strong></p>
<ul>
<li><strong>인터페이스 (Interface):</strong> <code>@interface</code> 로 선언되는 타입</li>
<li><strong>추상 메소드 (Abstract Method) 1개:</strong> 구현체가 없는 추상 메소드를 단 하나만 가짐</li>
<li><strong>default 메소드, static 메소드 허용:</strong> 추상 메소드 외에 default 메소드나 static 메소드는 여러 개 포함될 수 있습니다. (하지만 추상 메소드는 반드시 1개!)</li>
</ul>
<p><strong>함수형 인터페이스 예시:</strong></p>
<pre><code class="language-java">// 1. Runnable 인터페이스 (Java 기본 제공, 함수형 인터페이스)
@FunctionalInterface // 함수형 인터페이스임을 명시하는 어노테이션 (선택 사항)
public interface Runnable {
    void run(); // 추상 메소드 1개 (매개변수, 반환값 없음)
}

// 2. Comparator 인터페이스 (Java 기본 제공, 함수형 인터페이스)
@FunctionalInterface
public interface Comparator&lt;T&gt; {
    int compare(T o1, T o2); // 추상 메소드 1개 (매개변수 2개, int 반환값)
    // ... default 메소드, static 메소드 ...
}

// 3. MyFunctionalInterface (사용자 정의 함수형 인터페이스)
@FunctionalInterface
interface MyFunctionalInterface {
    int calculate(int a, int b); // 추상 메소드 1개 (매개변수 2개, int 반환값)
}</code></pre>
<h4 id="42-functionalinterface-어노테이션">4.2. <code>@FunctionalInterface</code> 어노테이션</h4>
<p><code>@FunctionalInterface</code> 어노테이션은 인터페이스가 <strong>함수형 인터페이스</strong>임을 명시적으로 선언하는 데 사용된다. <code>@FunctionalInterface</code> 어노테이션은 <strong>선택 사항</strong>이지만, 함수형 인터페이스를 만들 때는 <strong>붙여주는 것을 권장</strong>한다.</p>
<p><strong><code>@FunctionalInterface</code> 어노테이션의 장점:</strong></p>
<ul>
<li><strong>가독성 향상:</strong> 인터페이스가 함수형 인터페이스임을 명확하게 알려주어 코드 이해도를 높임.</li>
<li><strong>컴파일 에러 방지:</strong> <code>@FunctionalInterface</code> 어노테이션이 붙은 인터페이스가 함수형 인터페이스 조건을 만족하지 못하면 (추상 메소드가 2개 이상인 경우), 컴파일 에러를 발생시켜 개발자가 실수하는 것을 방지해 준다.</li>
</ul>
<h4 id="43-자바에서-제공하는-주요-함수형-인터페이스">4.3. 자바에서 제공하는 주요 함수형 인터페이스</h4>
<p>Java 8 에서는 람다식과 함께 다양한 용도로 활용할 수 있는 <strong>java.util.function 패키지</strong>를 통해 여러 종류의 함수형 인터페이스를 기본적으로 제공한다. 주요 함수형 인터페이스 몇 가지를 살펴보자.</p>
<table>
<thead>
<tr>
<th>함수형 인터페이스</th>
<th>추상 메소드</th>
<th>설명</th>
<th>람다식 형태 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>Runnable</code></td>
<td><code>void run()</code></td>
<td>매개변수 없고, 반환값 없는 작업 (쓰레드 실행, 이벤트 처리 등)</td>
<td><code>() -&gt; { ... }</code></td>
</tr>
<tr>
<td><code>Callable&lt;V&gt;</code></td>
<td><code>V call() throws Exception</code></td>
<td>매개변수 없고, 반환값 있는 작업 (비동기 작업 결과, Future와 함께 사용)</td>
<td><code>() -&gt; 값</code></td>
</tr>
<tr>
<td><code>Comparator&lt;T&gt;</code></td>
<td><code>int compare(T o1, T o2)</code></td>
<td>객체 비교 (정렬, 검색 등)</td>
<td><code>(o1, o2) -&gt; 비교 결과 (int)</code></td>
</tr>
<tr>
<td><code>Predicate&lt;T&gt;</code></td>
<td><code>boolean test(T t)</code></td>
<td>조건 검사 (필터링, 유효성 검증 등)</td>
<td><code>(t) -&gt; 조건 (boolean)</code></td>
</tr>
<tr>
<td><code>Function&lt;T, R&gt;</code></td>
<td><code>R apply(T t)</code></td>
<td>입력값 T를 받아서 값 R로 변환 (데이터 변환, 매핑 등)</td>
<td><code>(t) -&gt; 변환 결과 (R)</code></td>
</tr>
<tr>
<td><code>Consumer&lt;T&gt;</code></td>
<td><code>void accept(T t)</code></td>
<td>입력값 T를 받아서 소비 (출력, 로깅, 특정 동작 실행 등), 반환값 없음</td>
<td><code>(t) -&gt; { ... }</code></td>
</tr>
<tr>
<td><code>Supplier&lt;T&gt;</code></td>
<td><code>T get()</code></td>
<td>값을 제공 (생성, 획득 등), 매개변수 없이 값 T 반환</td>
<td><code>() -&gt; 값 (T)</code></td>
</tr>
</tbody></table>
<p><strong>함수형 인터페이스 활용:</strong></p>
<ul>
<li>람다식은 <strong>함수형 인터페이스 타입</strong>으로만 대입될 수 있다.</li>
<li>람다식은 함수형 인터페이스의 <strong>추상 메소드를 구현</strong>하는 익명 구현 객체를 생성.</li>
<li>함수형 인터페이스를 인자로 받는 메소드는 람다식을 인자로 받을 수 있다.</li>
</ul>
<p><strong>함수형 인터페이스는 람다식을 효과적으로 사용하기 위한 핵심적인 타입 시스템을 제공한다.</strong> 람다식을 통해 간결하게 표현된 익명 함수를 함수형 인터페이스 타입으로 다루면서, Java는 함수형 프로그래밍 패러다임을 더욱 강력하게 지원할 수 있게 되었다.</p>
<h3 id="5-람다식-활용-예제-실전-코드-완벽-분석">5. 람다식 활용 예제 (실전 코드 완벽 분석)</h3>
<p>람다식이 실제로 어떻게 활용되는지 다양한 예제 코드를 통해 자세히 살펴보자.</p>
<h4 id="51-익명-내부-클래스-대체-익명-함수처럼-간결하게">5.1. 익명 내부 클래스 대체 (익명 함수처럼 간결하게!)</h4>
<p>람다식은 익명 내부 클래스를 대체하여 코드를 더욱 간결하게 만들 수 있다. 특히 익명 내부 클래스가 함수형 인터페이스를 구현하는 경우, 람다식으로 훨씬 짧고 명료하게 코드를 작성할 수 있다.</p>
<h5 id="511-쓰레드-thread-생성">5.1.1. 쓰레드 (Thread) 생성</h5>
<p>쓰레드 생성 시 <code>Runnable</code> 함수형 인터페이스를 익명 내부 클래스로 구현하던 코드를 람다식으로 간결하게 바꿀 수 있다.</p>
<p><strong>기존 코드 (익명 내부 클래스):</strong></p>
<pre><code class="language-java">// 쓰레드 생성 (익명 내부 클래스)
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(&quot;쓰레드 실행 (익명 내부 클래스)&quot;);
    }
});
thread.start();</code></pre>
<p><strong>람다식 코드:</strong></p>
<pre><code class="language-java">// 쓰레드 생성 (람다식)
Thread thread = new Thread(() -&gt; { // Runnable 함수형 인터페이스를 람다식으로 구현
    System.out.println(&quot;쓰레드 실행 (람다식)&quot;);
});
thread.start();</code></pre>
<p><strong>람다식으로 코드가 훨씬 간결해진 것을 확인할 수 있다.</strong> 익명 내부 클래스 선언, <code>@Override</code> 어노테이션, 메소드 이름(<code>run</code>) 등 불필요한 코드를 줄이고, 핵심 로직 (System.out.println()) 에 집중할 수 있도록 코드를 개선했다.</p>
<h5 id="512-이벤트-핸들러-event-handler-구현">5.1.2. 이벤트 핸들러 (Event Handler) 구현</h5>
<p>GUI 프로그래밍 (Swing, JavaFX) 또는 웹 프로그래밍 (JavaScript) 에서 이벤트 핸들러를 구현할 때 익명 내부 클래스를 많이 사용하는데, 이 또한 람다식으로 대체하여 코드를 간결하게 만들 수 있다. (JavaFX 예시)</p>
<p><strong>기존 코드 (익명 내부 클래스):</strong></p>
<pre><code class="language-java">Button button = new Button(&quot;클릭!&quot;);
button.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println(&quot;버튼 클릭 이벤트 발생 (익명 내부 클래스)&quot;);
    }
});</code></pre>
<p><strong>람다식 코드:</strong></p>
<pre><code class="language-java">Button button = new Button(&quot;클릭!&quot;);
button.setOnAction(event -&gt; { // EventHandler 함수형 인터페이스를 람다식으로 구현
    System.out.println(&quot;버튼 클릭 이벤트 발생 (람다식)&quot;);
});</code></pre>
<p><strong>람다식을 사용하면 이벤트 핸들러 코드를 훨씬 짧고 읽기 쉽게 만들 수 있다.</strong> 이벤트 처리 로직 (System.out.println()) 에 더욱 집중할 수 있도록 코드를 개선하고, 불필요한 코드 작성을 줄여 개발 생산성을 향상시켰다.</p>
<h4 id="52-컬렉션-collection-처리-간결화-stream-api와-함께-더욱-강력하게">5.2. 컬렉션 (Collection) 처리 간결화 (Stream API와 함께 더욱 강력하게!)</h4>
<p>람다식은 Java 8 에서 함께 도입된 <strong>Stream API</strong> 와 함께 사용할 때 컬렉션 데이터 처리를 더욱 강력하고 효율적으로 만들어준다. Stream API 는 컬렉션 데이터를 <strong>선언형 (declarative)</strong> 방식으로 처리할 수 있도록 지원하며, 람다식은 Stream API 에서 데이터 처리 로직을 간결하게 표현하는 데 핵심적인 역할을 한다.</p>
<h5 id="521-foreach-각-요소-순회">5.2.1. <code>forEach</code> (각 요소 순회)</h5>
<p>컬렉션의 각 요소에 대해 특정 동작을 수행할 때 <code>forEach</code> 메소드와 람다식을 함께 사용하면 코드를 간결하게 만들 수 있습니다.</p>
<p><strong>기존 코드 (for-each 루프):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// for-each 루프를 사용한 요소 순회
for (String name : names) {
    System.out.println(&quot;이름: &quot; + name);
}</code></pre>
<p><strong>람다식 코드 (<code>forEach</code> 메소드):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// forEach 메소드와 람다식을 사용한 요소 순회
names.forEach(name -&gt; { // Consumer 함수형 인터페이스를 람다식으로 구현
    System.out.println(&quot;이름 (forEach + 람다식): &quot; + name);
});</code></pre>
<p><strong>람다식을 사용하면 컬렉션 요소 순회 코드를 한 줄로 간결하게 표현할 수 있다.</strong> 
for-each 루프에 비해 코드 라인 수를 줄이고, 코드 가독성을 높여준다.</p>
<h5 id="522-map-요소-변환">5.2.2. <code>map</code> (요소 변환)</h5>
<p>컬렉션의 각 요소를 특정 기준으로 변환하여 새로운 컬렉션을 만들 때 <code>map</code> 메소드와 람다식을 함께 사용한다.</p>
<p><strong>기존 코드 (for-each 루프 + 새로운 리스트 생성):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);
List&lt;Integer&gt; nameLengths = new ArrayList&lt;&gt;();

// for-each 루프를 사용한 요소 변환
for (String name : names) {
    nameLengths.add(name.length()); // 이름 길이를 새로운 리스트에 추가
}
System.out.println(&quot;이름 길이 리스트: &quot; + nameLengths);</code></pre>
<p><strong>람다식 코드 (<code>map</code> 메소드):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// map 메소드와 람다식을 사용한 요소 변환
List&lt;Integer&gt; nameLengths = names.stream() // Stream 생성
        .map(name -&gt; name.length()) // Function 함수형 인터페이스를 람다식으로 구현 (이름 -&gt; 이름 길이)
        .collect(Collectors.toList()); // List로 수집
System.out.println(&quot;이름 길이 리스트 (map + 람다식): &quot; + nameLengths);</code></pre>
<p><strong>람다식을 사용하면 컬렉션 요소 변환 코드를 더욱 간결하고 선언적으로 표현할 수 있다.</strong> Stream API 의 <code>map</code> 메소드는 각 요소에 람다식을 적용하여 변환된 요소들을 새로운 Stream 으로 반환하고, <code>collect(Collectors.toList())</code> 메소드는 Stream 을 다시 List 로 변환한다. 복잡한 데이터 변환 로직을 짧은 코드로 구현할 수 있다.</p>
<h5 id="523-filter-요소-필터링">5.2.3. <code>filter</code> (요소 필터링)</h5>
<p>컬렉션에서 특정 조건을 만족하는 요소만 골라 새로운 컬렉션을 만들 때 <code>filter</code> 메소드와 람다식을 함께 사용한다.</p>
<p><strong>기존 코드 (for-each 루프 + 조건 검사 + 새로운 리스트 생성):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;);
List&lt;String&gt; longNames = new ArrayList&lt;&gt;();

// for-each 루프와 조건 검사를 사용한 요소 필터링
for (String name : names) {
    if (name.length() &gt; 5) { // 이름 길이가 5보다 큰 경우
        longNames.add(name); // 새로운 리스트에 추가
    }
}
System.out.println(&quot;긴 이름 리스트: &quot; + longNames);</code></pre>
<p><strong>람다식 코드 (<code>filter</code> 메소드):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;);

// filter 메소드와 람다식을 사용한 요소 필터링
List&lt;String&gt; longNames = names.stream() // Stream 생성
        .filter(name -&gt; name.length() &gt; 5) // Predicate 함수형 인터페이스를 람다식으로 구현 (이름 길이 &gt; 5)
        .collect(Collectors.toList()); // List로 수집
System.out.println(&quot;긴 이름 리스트 (filter + 람다식): &quot; + longNames);</code></pre>
<p><strong>람다식을 사용하면 컬렉션 요소 필터링 코드를 매우 간결하고 직관적으로 표현할 수 있다.</strong> Stream API 의 <code>filter</code> 메소드는 각 요소에 람다식을 적용하여 조건이 참인 요소만 Stream 으로 반환하고, <code>collect(Collectors.toList())</code> 메소드는 Stream 을 다시 List 로 변환합니다. 복잡한 조건 필터링 로직을 짧은 코드로 구현할 수 있다.</p>
<h5 id="524-reduce-요소-집계">5.2.4. <code>reduce</code> (요소 집계)</h5>
<p>컬렉션의 요소들을 특정 연산을 통해 하나의 값으로 집계할 때 <code>reduce</code> 메소드와 람다식을 함께 사용한다.</p>
<p><strong>기존 코드 (for-each 루프 + 누적 변수):</strong></p>
<pre><code class="language-java">List&lt;Integer&gt; numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;

// for-each 루프를 사용한 요소 합계 계산
for (int number : numbers) {
    sum += number; // 누적 변수에 합산
}
System.out.println(&quot;숫자 합계: &quot; + sum);</code></pre>
<p><strong>람다식 코드 (<code>reduce</code> 메소드):</strong></p>
<pre><code class="language-java">List&lt;Integer&gt; numbers = Arrays.asList(1, 2, 3, 4, 5);

// reduce 메소드와 람다식을 사용한 요소 합계 계산
int sum = numbers.stream() // Stream 생성
        .reduce(0, (a, b) -&gt; a + b); // BinaryOperator 함수형 인터페이스를 람다식으로 구현 (누적 연산)
System.out.println(&quot;숫자 합계 (reduce + 람다식): &quot; + sum);</code></pre>
<p><strong>람다식을 사용하면 컬렉션 요소 집계 코드를 매우 간결하고 유연하게 표현할 수 있다.</strong> Stream API 의 <code>reduce</code> 메소드는 초기값 (0) 과 람다식을 인자로 받아서, 각 요소에 대해 람다식을 누적 적용하여 최종 결과값을 반환한다. 합계, 평균, 최대/최소값, 사용자 정의 집계 등 다양한 연산을 간결하게 구현할 수 있다.</p>
<h5 id="525-sort-정렬">5.2.5. <code>sort</code> (정렬)</h5>
<p>컬렉션을 특정 기준으로 정렬할 때 <code>sort</code> 메소드와 람다식 (Comparator 함수형 인터페이스 구현) 을 함께 사용합니다.</p>
<p><strong>기존 코드 (익명 내부 클래스 Comparator 구현):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Charlie&quot;, &quot;Alice&quot;, &quot;Bob&quot;, &quot;David&quot;);

// 익명 내부 클래스를 사용한 Comparator 구현 (이름 길이 기준 오름차순 정렬)
Collections.sort(names, new Comparator&lt;String&gt;() {
    @Override
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length()); // 이름 길이 비교
    }
});
System.out.println(&quot;정렬된 이름 리스트 (익명 내부 클래스): &quot; + names);</code></pre>
<p><strong>람다식 코드 (<code>sort</code> 메소드 + 람다식 Comparator 구현):</strong></p>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Charlie&quot;, &quot;Alice&quot;, &quot;Bob&quot;, &quot;David&quot;);

// sort 메소드와 람다식을 사용한 Comparator 구현 (이름 길이 기준 오름차순 정렬)
Collections.sort(names, (s1, s2) -&gt; Integer.compare(s1.length(), s2.length())); // Comparator 함수형 인터페이스를 람다식으로 구현
System.out.println(&quot;정렬된 이름 리스트 (람다식): &quot; + names);

// 더 간결하게 메소드 참조 사용 가능
names.sort(Comparator.comparingInt(String::length)); // Comparator.comparingInt 메소드 + 메소드 참조
System.out.println(&quot;정렬된 이름 리스트 (메소드 참조): &quot; + names);</code></pre>
<p><strong>람다식을 사용하면 컬렉션 정렬 기준을 훨씬 간결하고 직관적으로 표현할 수 있다.</strong> <code>Collections.sort</code> 메소드 또는 List 자체의 <code>sort</code> 메소드에 람다식으로 구현된 <code>Comparator</code> 를 전달하여 정렬 기준을 유연하게 정의할 수 있다. 메소드 참조 (<code>String::length</code>) 를 함께 사용하면 코드를 더욱 간결하게 만들 수 있다.</p>
<p><strong>Stream API 와 람다식을 함께 사용하면 컬렉션 데이터 처리 코드를 극적으로 간결하고 효율적으로 만들 수 있다.</strong> 데이터 필터링, 변환, 집계, 정렬 등 복잡한 데이터 처리 로직을 짧고 가독성 높은 코드로 구현하여 개발 생산성을 크게 향상시킬 수 있다.</p>
<h3 id="6-람다식의-강력한-장점-코드-간결성-가독성-생산성-향상">6. 람다식의 강력한 장점 (코드 간결성, 가독성, 생산성 향상!)</h3>
<p>람다식은 Java 코드 작성 방식을 혁신적으로 변화시키고, 다양한 장점을 제공한다.</p>
<ul>
<li><p><strong>코드 간결성 및 가독성 향상:</strong></p>
<ul>
<li>익명 내부 클래스에 비해 코드를 훨씬 짧고 간결하게 표현 가능</li>
<li>불필요한 코드 (클래스 선언, 메소드 이름 등) 를 줄여 코드 라인 수 감소</li>
<li>코드의 핵심 로직에 집중할 수 있도록 가독성 향상</li>
<li>특히 짧은 함수 로직이나 콜백 함수를 표현할 때 효과적</li>
</ul>
</li>
<li><p><strong>함수형 프로그래밍 패러다임 지원:</strong></p>
<ul>
<li>Java 를 함수형 프로그래밍 방식으로 코딩할 수 있도록 지원</li>
<li>불변성 (Immutability), 순수 함수 (Pure Function), 고차 함수 (Higher-Order Function) 와 같은 함수형 프로그래밍 개념 활용 가능</li>
<li>Stream API 와 함께 데이터 처리 로직을 선언형 (declarative) 방식으로 표현 가능</li>
<li>side effect 를 줄이고, 코드의 예측 가능성 및 테스트 용이성 향상</li>
</ul>
</li>
<li><p><strong>병렬 처리 효율 증대 (Stream API와 함께):</strong></p>
<ul>
<li>Stream API 와 람다식을 함께 사용하면 컬렉션 데이터를 병렬로 처리하는 코드를 쉽게 작성 가능</li>
<li>병렬 스트림 (<code>parallelStream()</code>) 을 사용하여 멀티 코어 CPU 환경에서 데이터 처리 성능 극대화</li>
<li>대용량 데이터 처리, 빅데이터 분석 등 성능이 중요한 분야에서 람다식과 Stream API 의 장점 극대화</li>
</ul>
</li>
<li><p><strong>개발 생산성 향상:</strong></p>
<ul>
<li>코드 작성 시간 단축, 코드 리뷰 및 유지보수 효율 증가</li>
<li>버그 발생 가능성 감소, 코드 품질 향상</li>
</ul>
</li>
</ul>
<h3 id="7-람다식-사용-시-주의사항-제약-사항-및-한계점">7. 람다식 사용 시 주의사항 (제약 사항 및 한계점)</h3>
<p>람다식은 강력한 기능이지만, 몇 가지 주의사항과 한계점도 존재한다.  </p>
<ul>
<li><strong><code>this</code> 키워드 동작 방식 차이:</strong><ul>
<li>익명 내부 클래스의 <code>this</code> 는 익명 내부 클래스 객체 자신을 가리키지만, 람다식의 <code>this</code> 는 람다식을 감싸는 외부 클래스 객체를 가리킨다. 람다식 내부에서 <code>this</code> 를 사용하는 경우, 익명 내부 클래스와 다른 동작 방식에 주의해야 한다.</li>
</ul>
</li>
<li><strong>디버깅 (Debugging) 어려움:</strong><ul>
<li>람다식은 익명 함수이기 때문에, 디버깅 시 스택 트레이스 (stack trace) 를 추적하기 어려울 수 있다. 특히 복잡한 람다식 체인 (Stream API 파이프라인) 에서 에러가 발생하면 디버깅이 까다로워질 수 있다.</li>
</ul>
</li>
<li><strong>과도한 사용은 오히려 가독성 저하:</strong><ul>
<li>람다식을 너무 과도하게 사용하거나, 너무 복잡한 로직을 람다식 하나로 표현하려고 하면 오히려 코드 가독성이 떨어질 수 있다. 람다식은 간결하고 짧은 함수 로직에 적합하며, 복잡한 로직은 적절히 메소드로 분리하는 것이 좋다.</li>
</ul>
</li>
<li><strong>모든 인터페이스에 람다식을 사용할 수 있는 것은 아님:</strong><ul>
<li>람다식은 <strong>함수형 인터페이스</strong> 타입으로만 대입될 수 있다. 함수형 인터페이스가 아닌 일반적인 인터페이스나 클래스에는 람다식을 사용할 수 없다.</li>
</ul>
</li>
</ul>
<h3 id="8-람다식-vs-익명-내부-클래스-차이점-명확-비교">8. 람다식 vs 익명 내부 클래스 (차이점 명확 비교)</h3>
<p>람다식은 익명 내부 클래스를 간결하게 대체하는 기능이지만, 몇 가지 중요한 차이점이 존재한다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>람다식 (Lambda Expression)</th>
<th>익명 내부 클래스 (Anonymous Inner Class)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>목적</strong></td>
<td>함수형 인터페이스의 <strong>단일 추상 메소드 구현</strong> (익명 함수)</td>
<td><strong>클래스 또는 인터페이스 상속/구현</strong> (익명 객체 생성)</td>
</tr>
<tr>
<td><strong>코드 간결성</strong></td>
<td>매우 간결 (익명 내부 클래스 대비 코드 라인 수 현저히 감소)</td>
<td>상대적으로 복잡 (클래스 선언, 메소드 구현, <code>@Override</code> 등 필요)</td>
</tr>
<tr>
<td><strong><code>this</code> 키워드</strong></td>
<td><strong>외부 클래스 객체</strong> 참조</td>
<td><strong>익명 내부 클래스 객체 자신</strong> 참조</td>
</tr>
<tr>
<td><strong>변수 캡처링 (Variable Capturing)</strong></td>
<td><strong>effectively final 변수</strong>만 캡처 가능 (값 변경 불가)</td>
<td><strong><code>final</code> 또는 effectively final 변수</strong> 캡처 가능 (Java 8 이전에는 <code>final</code> 만 가능)</td>
</tr>
<tr>
<td><strong>생성자</strong></td>
<td>람다식 자체는 생성자 없음 (객체 생성 메커니즘 다름)</td>
<td>익명 내부 클래스는 생성자 가질 수 있음</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>일반적으로 익명 내부 클래스보다 <strong>약간 더 성능 우수</strong> (람다식 컴파일 최적화)</td>
<td>람다식에 비해 성능 면에서 약간 불리할 수 있음</td>
</tr>
<tr>
<td><strong>주요 사용처</strong></td>
<td>함수형 인터페이스 구현, Stream API, 콜백 함수, 이벤트 핸들러 (간단한 로직)</td>
<td>함수형 인터페이스 구현, 복잡한 로직, 상태를 가지는 객체, 다중 메소드 인터페이스 구현, 익명 객체 생성 필요한 모든 경우</td>
</tr>
</tbody></table>
<p><strong>람다식과 익명 내부 클래스 선택 가이드:</strong></p>
<ul>
<li><strong>함수형 인터페이스 구현, 간단한 함수 로직:</strong> 람다식 (더 간결하고 효율적)</li>
<li><strong>함수형 인터페이스 구현, 복잡한 함수 로직, 상태 관리, 다중 메소드 인터페이스 구현:</strong> 익명 내부 클래스 또는 일반 클래스 (유연성 확보)</li>
<li><strong>익명 객체 생성, 클래스 상속/구현:</strong> 익명 내부 클래스 (람다식으로 대체 불가능)</li>
</ul>
<h3 id="9-람다식-언제-어디에-사용해야-할까-활용-가이드라인">9. 람다식, 언제 어디에 사용해야 할까? (활용 가이드라인)</h3>
<p>람다식을 무분별하게 사용하는 것은 오히려 코드 가독성을 해치고 유지보수를 어렵게 만들 수 있다. 여기서는 람다식을 효과적으로 사용하기 위한 가이드라인을 제시한다.</p>
<ul>
<li><strong>람다식을 사용하는 것이 효과적인 경우:</strong><ul>
<li><strong>함수형 인터페이스를 구현하는 익명 객체를 간결하게 생성하고 싶을 때</strong></li>
<li><strong>짧고 간단한 함수 로직 (1~2 줄)</strong> 을 표현할 때 (표현식 람다 몸체 활용)</li>
<li><strong>컬렉션 데이터 처리 (Stream API)</strong> 와 함께 사용하여 데이터 변환, 필터링, 집계 등 연산을 간결하게 표현하고 싶을 때</li>
<li><strong>콜백 함수, 이벤트 핸들러</strong> 등 함수를 인자로 전달하는 상황에서 코드 간결성을 높이고 싶을 때</li>
</ul>
</li>
<li><strong>람다식 사용을 지양해야 하거나 신중해야 하는 경우:</strong><ul>
<li><strong>람다 몸체가 너무 길거나 복잡한 로직을 포함하는 경우:</strong> 람다식을 메소드로 분리하여 코드 가독성을 높이는 것이 좋다.</li>
<li><strong>람다식 내부에서 <code>this</code> 키워드를 사용하는 경우:</strong> 람다식의 <code>this</code> 와 익명 내부 클래스의 <code>this</code> 동작 방식이 다르므로, <code>this</code> 사용에 주의해야 한다.</li>
<li><strong>디버깅이 어려워질 수 있는 복잡한 람다식 체인 (Stream API 파이프라인) 을 사용하는 경우:</strong> 코드 가독성을 해치지 않도록 적절히 코드를 분리하고, 디버깅 전략을 미리 고려해야 한다.</li>
<li><strong>함수형 인터페이스가 아닌 일반적인 인터페이스나 클래스를 구현해야 하는 경우:</strong> 람다식으로는 구현할 수 없으므로 익명 내부 클래스 또는 일반 클래스를 사용해야 한다.</li>
</ul>
</li>
</ul>
<p><strong>람다식 활용 핵심:</strong> <strong>&quot;코드 간결성, 가독성, 유지보수성 향상&quot;</strong> 에 긍정적인 효과를 줄 수 있는 경우에 람다식을 적극적으로 활용하고, 그렇지 않은 경우에는 기존 방식 (익명 내부 클래스, 일반 클래스) 과 람다식을 적절히 혼용하여 사용하는 것이 좋다.</p>
<h3 id="10-마무리-정리">10. 마무리 (정리)</h3>
<p>Java 람다식에 대한 모든 것을 자세하게 알아보았다. 람다식은 Java 개발 방식을 혁신적으로 변화시키고, 코드를 더욱 간결하고 유연하며 강력하게 만들어주는 핵심 기능이다.</p>
<p><strong>핵심 정리:</strong></p>
<ul>
<li><strong>람다식:</strong> 익명 함수를 간결하게 표현하는 방법, 함수형 프로그래밍 지원</li>
<li><strong>문법:</strong> <code>(파라미터) -&gt; { 람다 몸체 }</code> (표현식 몸체, 블록 몸체)</li>
<li><strong>함수형 인터페이스:</strong> 람다식을 담는 그릇, 추상 메소드 1개 인터페이스</li>
<li><strong>활용:</strong> 익명 내부 클래스 대체, Stream API 와 함께 컬렉션 처리 간결화, 이벤트 핸들러, 콜백 함수 등</li>
<li><strong>장점:</strong> 코드 간결성, 가독성, 생산성 향상, 함수형 프로그래밍, 병렬 처리 효율 증대</li>
<li><strong>주의사항:</strong> <code>this</code> 동작 방식, 디버깅, 과도한 사용, 함수형 인터페이스 타입 제한</li>
<li><strong>람다식 vs 익명 내부 클래스:</strong> 목적, 코드 스타일, <code>this</code>, 변수 캡처링, 성능 등 차이점 존재</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[@RequestBody란?]]></title>
            <link>https://velog.io/@sanghyeok_moon/RequestBody%EB%9E%80</link>
            <guid>https://velog.io/@sanghyeok_moon/RequestBody%EB%9E%80</guid>
            <pubDate>Thu, 06 Feb 2025 15:20:47 GMT</pubDate>
            <description><![CDATA[<p><code>@RequestBody</code>는 Spring Framework에서 <strong>HTTP 요청의 본문(body)을 자바 객체로 변환하기 위해 사용되는 어노테이션</strong>이다. 
주로 RESTful API에서 <strong>클라이언트가 전송한 데이터(JSON, XML 등)</strong>를 서버 측에서 객체로 받을 때 활용된다.</p>
<h3 id="주요-특징-및-동작-방식">주요 특징 및 동작 방식</h3>
<ol>
<li><p><strong>HTTP Body 파싱</strong><br>요청의 본문(<code>body</code>)을 읽어 <strong>Java 객체로 변환</strong>한다.<br>예: 클라이언트가 <code>{&quot;name&quot;: &quot;John&quot;, &quot;age&quot;: 30}</code>을 전송하면, <code>User</code> 객체의 필드에 매핑된다.</p>
</li>
<li><p><strong>MessageConverter 사용</strong><br>Spring은 요청의 <code>Content-Type</code> 헤더를 기반으로 적절한 <code>HttpMessageConverter</code>를 선택한다.  </p>
<ul>
<li>JSON ➡️ <code>MappingJackson2HttpMessageConverter</code> (Jackson 라이브러리 사용)  </li>
<li>XML ➡️ <code>Jaxb2RootElementHttpMessageConverter</code>  </li>
<li>(Spring Boot는 자동으로 Jackson을 설정한다.)</li>
</ul>
</li>
<li><p><strong>사용 예시 (컨트롤러)</strong>  </p>
<pre><code class="language-java">@PostMapping(&quot;/users&quot;)
public ResponseEntity&lt;User&gt; createUser(@RequestBody User user) {
    // user 객체 활용 (ex: DB 저장)
    return ResponseEntity.ok(user);
}</code></pre>
</li>
<li><p><strong>유효성 검증</strong><br><code>@Valid</code>와 결합해 객체의 유효성을 검사할 수 있다.  </p>
<pre><code class="language-java">@PostMapping(&quot;/users&quot;)
public ResponseEntity&lt;?&gt; createUser(@Valid @RequestBody User user) {
    // 유효성 통과 시 로직 실행
}</code></pre>
</li>
</ol>
<h3 id="주의사항">주의사항</h3>
<ul>
<li><strong>HTTP 메서드</strong>: 주로 <code>POST</code>, <code>PUT</code>, <code>PATCH</code>와 같이 본문이 필요한 메서드에서 사용합니다. <code>GET</code>에서는 권장되지 않는다.</li>
<li><strong>Content-Type</strong>: 클라이언트는 요청 시 올바른 <code>Content-Type</code>(ex: <code>application/json</code>)을 헤더에 명시해야 한다.</li>
<li><strong>에러 처리</strong>: 잘못된 형식의 데이터 전송 시 <code>HttpMessageNotReadableException</code>이 발생하므로, 예외 핸들링이 필요하다.</li>
</ul>
<h3 id="활용-시나리오">활용 시나리오</h3>
<ul>
<li><strong>REST API 개발</strong>: 클라이언트(React, Angular 등)가 JSON 데이터를 전송할 때.</li>
<li><strong>마이크로서비스 통신</strong>: 서비스 간 데이터를 객체로 직렬화/역직렬화할 때.</li>
<li><strong>유효성 검증</strong>: 전송 데이터의 필수 값, 형식 등을 검증할 때 <code>@Valid</code>와 함께 사용.</li>
</ul>
<h3 id="예제-코드-json-요청">예제 코드 (JSON 요청)</h3>
<p><strong>요청</strong></p>
<pre><code>POST /users HTTP/1.1
Content-Type: application/json

{
  &quot;name&quot;: &quot;Alice&quot;,
  &quot;email&quot;: &quot;alice@example.com&quot;
}</code></pre><p><strong>컨트롤러</strong></p>
<pre><code class="language-java">@RestController
public class UserController {
    @PostMapping(&quot;/users&quot;)
    public User createUser(@RequestBody User user) {
        // user 객체 처리
        return user;
    }
}</code></pre>
<p><strong>User 클래스</strong></p>
<pre><code class="language-java">public class User {
    private String name;
    private String email;
    // getter, setter, 생성자
}</code></pre>
<p>이렇게 하면 Spring이 자동으로 JSON을 <code>User</code> 객체로 변환해 전달한다.</p>
<p>즉, @RequestBody란? 클라이언트가 전송한 사용자 정보를 DTO로 매핑하여 메서드의 매개변수로 전달하는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MVC 패턴이란?]]></title>
            <link>https://velog.io/@sanghyeok_moon/MVC-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@sanghyeok_moon/MVC-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Thu, 06 Feb 2025 10:09:33 GMT</pubDate>
            <description><![CDATA[<p><strong>MVC(Model-View-Controller)</strong> 패턴은 애플리케이션을 <strong>데이터 관리, 사용자 인터페이스(UI), 사용자 입력 처리</strong>의 세 가지 역할로 분리하여 설계하는 아키텍처 패턴이다. 각 구성 요소의 역할과 상호작용을 명확히 함으로써 유지보수성, 확정성, 재사용성을 높인다.</p>
<hr>
<h2 id="1-구성-요소별-역할"><strong>1. 구성 요소별 역할</strong></h2>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>설명</th>
<th>주요 책임</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Model</strong></td>
<td>애플리케이션의 <strong>데이터와 비즈니스 로직</strong>을 관리합니다.</td>
<td>- 데이터 저장, 조회, 수정 <br>- 변경 시 View/Controller에 알림(Observer 패턴 주로 활용)</td>
</tr>
<tr>
<td><strong>View</strong></td>
<td>사용자에게 정보를 표시하는 <strong>UI 레이어</strong>입니다.</td>
<td>- Model의 데이터를 시각화 <br>- 사용자 입력을 Controller에 전달</td>
</tr>
<tr>
<td><strong>Controller</strong></td>
<td>사용자 입력을 처리하고 <strong>Model과 View를 중개</strong>합니다.</td>
<td>- 입력 검증 및 처리 <br>- Model 업데이트 또는 데이터 조회 <br>- View 선택 및 업데이트</td>
</tr>
</tbody></table>
<hr>
<h2 id="2-동작-흐름"><strong>2. 동작 흐름</strong></h2>
<ol>
<li><p><strong>사용자 입력</strong>: View를 통해 입력(예: 버튼 클릭)이 발생하면 <strong>Controller</strong>로 전달됩니다.</p>
</li>
<li><p><strong>입력 처리</strong>: Controller는 입력을 해석하고, 필요한 경우 <strong>Model</strong>을 업데이트하거나 데이터를 요청합니다.</p>
</li>
<li><p><strong>데이터 변경</strong>: Model은 데이터 변경 후 <strong>관련 View</strong>를 자동으로 갱신하거나(Observer 패턴) Controller에 알립니다.</p>
</li>
<li><p><strong>UI 갱신</strong>: Controller는 최신 데이터를 기반으로 <strong>View를 선택하거나 업데이트</strong>하여 사용자에게 표시합니다.</p>
</li>
</ol>
<hr>
<h2 id="3-장점"><strong>3. 장점</strong></h2>
<ul>
<li><p><strong>관심사 분리(Separation of Concerns)</strong>: 각 컴포넌트가 독립적으로 작동하여 코드 복잡도 감소.</p>
</li>
<li><p><strong>재사용성</strong>: Model은 UI 변경 없이 재사용 가능하며, View는 다른 데이터 소스와 연결 가능합니다.</p>
</li>
<li><p><strong>유지보수 용이</strong>: 기능 수정 시 해당 컴포넌트만 변경하면 됩니다.</p>
</li>
<li><p><strong>협업 효율성</strong>: 프론트엔드/백엔드 개발자의 병렬 작업이 용이합니다.</p>
</li>
</ul>
<hr>
<h2 id="4-단점"><strong>4. 단점</strong></h2>
<ul>
<li><p><strong>초기 복잡성</strong>: 간단한 애플리케이션에는 과도한 구조일 수 있습니다.</p>
</li>
<li><p><strong>의존성 관리</strong>: View와 Model이 직접 통신하면 <strong>결합도 증가</strong> (이를 해결하기 위해 MVVM 등 파생 패턴 등장).</p>
</li>
<li><p><strong>컨트롤러 과부하</strong>: 로직이 Controller에 집중되면 &quot;뚱뚱한 컨트롤러&quot; 문제 발생 가능.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[UTF-8이란?]]></title>
            <link>https://velog.io/@sanghyeok_moon/UTF-8%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@sanghyeok_moon/UTF-8%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Thu, 06 Feb 2025 08:27:33 GMT</pubDate>
            <description><![CDATA[<p>UTF-8은 문자 인코딩 방식 중 하나로, 유니코드(Unicode)를 기반으로 하여 다양한 문자를 표현할 수 있도록 설계되었다. UTF-8의 주요 특징은 다음과 같다.</p>
<h2 id="주요-특징">주요 특징</h2>
<ol>
<li><p><strong>가변 길이 인코딩</strong>:</p>
<ul>
<li>UTF-8은 <strong>1바이트 에서 4바이트까지 가변 길이로 문자를 인코딩</strong>한다. 기본 ASCII 문자는 1바이트로 표현되고, 그 외의 문자는 2바이트에서 4바이트로 표현된다.</li>
</ul>
</li>
<li><p><strong>ASCII 호환성</strong>:</p>
<ul>
<li>UTF-8은 ASCII와 호환된다. 즉, ASCII 코드의 0~127번까지의 문자는 UTF-8로 표현할 때 동일한 바이트 값을 가진다. 이는 기존의 ASCII 기반 시스템과의 호환성을 제공한다.</li>
</ul>
</li>
<li><p><strong>다양한 문자 지원</strong>:</p>
<ul>
<li>UTF-8은 전 세계의 다양한 문자를 지원하며, 모든 유니코드 문자를 표현할 수 있다. 이는 다국어 웹사이트 및 애플리케이션 개발에 유리하다.</li>
</ul>
</li>
</ol>
<h2 id="사용-예">사용 예</h2>
<ul>
<li><p>웹 페이지, 데이터베이스 , 파일 등 다양한 분야에서 UTF-8 인코딩이 널리 사용된다.</p>
</li>
<li><p>HTML에서 <meta charset="UTF-8"> 태그를 통해 UTF-8 인코딩을 설정할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[IP주소와 포트번호]]></title>
            <link>https://velog.io/@sanghyeok_moon/IP%EC%A3%BC%EC%86%8C%EC%99%80-%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8</link>
            <guid>https://velog.io/@sanghyeok_moon/IP%EC%A3%BC%EC%86%8C%EC%99%80-%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8</guid>
            <pubDate>Thu, 06 Feb 2025 08:18:39 GMT</pubDate>
            <description><![CDATA[<h2 id="ip-주소">IP 주소</h2>
<ul>
<li><p><strong>정의</strong> : IP 주소는 <strong>네트워크에 연결된 장치의 고유한 주소</strong> 를 의미한다. 인터넷 상에서 장치들이 서로를 식별하고 통신하기 위해 필요하다.</p>
</li>
<li><p><strong>형식</strong> :</p>
<ul>
<li><p><strong>IPv4</strong> : 192.168.1.1과 같은 형식으로, 4개의 8비트 숫자로 구성</p>
</li>
<li><p><strong>IPv6</strong> : 2001:0db8:85a3:0000:0000:8a2e:0370:7334와 같은 형식으로, 128비트 주소</p>
</li>
</ul>
</li>
</ul>
<h2 id="포트-번호">포트 번호</h2>
<ul>
<li><p><strong>정의</strong> : 포트 번호는 <strong>특정 IP 주소 내에서 어떤 서비스나 애플리케이션이 실행되고 있는지를 구분하는 번호</strong>. 각 서비스는 고유한 포트 번호를 사용한다.</p>
</li>
<li><p><strong>형식</strong> : 포트 번호는 <strong>0부터 65535</strong> 까지의 값을 가질 수 있다. 0~1023번 포트는 잘 알려진 서비스가 사용한다. <strong>(HTTP: 80번, HTTPS: 443번 등)</strong></p>
</li>
</ul>
<h2 id="ip-주소와-포트-번호의-조합">IP 주소와 포트 번호의 조합</h2>
<ul>
<li><strong>형식</strong> : IP 주소와 포트 번호는 함께 사용되어 특정 장치 내의 특정 서비스에 접근하는 데 사용된다. 예를 들어 192.168.1.1:8080은 IP 주소 192.168.1.1의 8080번 포트에서 실행 중인 서비스에 접근하는 것을 의미한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>