<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>woomin-wang.log</title>
        <link>https://velog.io/</link>
        <description>Backend Developer</description>
        <lastBuildDate>Mon, 14 Jul 2025 08:13:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>woomin-wang.log</title>
            <url>https://velog.velcdn.com/images/woomin-wang/profile/f8f0b0b7-2af3-4e74-a1b2-159df5fa4c6d/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. woomin-wang.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/woomin-wang" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] Record]]></title>
            <link>https://velog.io/@woomin-wang/Java-Record</link>
            <guid>https://velog.io/@woomin-wang/Java-Record</guid>
            <pubDate>Mon, 14 Jul 2025 08:13:12 GMT</pubDate>
            <description><![CDATA[<p>Record에 대해 본격적으로 학습하기 전에, 먼저 Record가 왜 필요하지 이해하기 위해 <strong>Entity</strong>와 <strong>DTO</strong>라는 두 가지 핵심 개념부터 알아보자.</p>
<h2 id="entity">Entity</h2>
<p>애플리케이션에서 가장 중요한 데이터는 <strong>Entity</strong>로 표현된다.
<strong>Entity는 데이터베이스 테이블의 &#39;원본 데이터&#39;와 일대일로 매핑되는 객체이다.</strong>
예를 들어 <code>User</code> Entity는 데이터베이스의 <code>user</code> 테이블과 연결되어 사용자의 모든 정보를 담고 있다.</p>
<p>Entity는 단순히 데이터를 저장하는 것을 넘어, 비즈니스 규칙에 따라 상태가 변해야 할 필요가 있다.
<code>주문</code> Entity가 <code>결제 완료</code>에서 <code>배송 중</code>으로 상태를 바꾸거나, <code>상품</code> Entity의 재고 수량이 변하는 것처럼 말이다.
이러한 이유 때문에 <strong>Entity는 가변적으로 설계</strong>되며, 직접적인 비즈니스 로직을 포함하는 경우가 많다.</p>
<br>
<br>


<h2 id="dto-data-transfer-object">DTO (Data Transfer Object)</h2>
<p>애플리케이션은 여러 계층으로 나뉘어 있다. 이 계층들 사이에서 데이터를 주고받을 때, Entity 원본을 그대로 전달하면 보안이나 불필요한 정보 노출 문제가 발생할 수 있다.</p>
<p>이때 사용되는 것이 바로 DTO(Data Transfer Object)이다. DTO는 계층 간에 데이터를 안전하게 전달하기 위한 &#39;복사본&#39; 역할을 한다.
<strong>DTO는 클라이언트에게 필요한 정보만 선별적으로 담고, 민감한 정보는 제외한다.</strong>
이처럼 DTO는 순수한 데이터 전달용이므로 비즈니스 로직을 포함하지 않으며, 데이터의 안정성을 위해 상태가 변하지 않는 <strong>불변 객체로 설계</strong>되는 것이 일반적이다.</p>
<br>
<br>

<h2 id="record">Record</h2>
<p>DTO는 불변 객체여야 하지만, 기존 자바 클래스로 불변 DTO를 만들려면 필드, 생성자, <code>getter</code>, <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code> 등 반복적인 코드를 직접 작성해야 하는 번거로움이 있었다.</p>
<p>이처럼 반복적으로 작성해야 하는 코드를 <strong>보일러플레이트 코드</strong>라고 하며, 
자바 14에서 도입된 <strong>Record</strong>는 이 문제를 해결하기 위해 등장했다.</p>
<blockquote>
<p> 💡 <em><strong>보일러 플레이트 코드</strong></em></p>
<p>소프트웨어 개발에서 반복적으로 작성되지만, 특별한 의미나 로직을 담고 있지 않은 코드를 말한다.</p>
<p>예시: DTO를 만들 때 작성하는 <code>getter</code>, <code>setter</code>, <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code> 메서드가 대표적인 보일러플레이트 코드에 해당한다.</p>
</blockquote>
<p>다음과 같은 특성 덕분에 Record는 DTO의 모든 요구사항을 완벽하게 충족시킨다.</p>
<ul>
<li><p><strong>불변성 자동 보장</strong>: Record는 선언과 동시에 모든 컴포넌트가 <code>final</code>로 설정되어 불변성이 보장된다.</p>
</li>
<li><p><strong>코드 간소화</strong>: Record는 컴포넌트만 정의하면 생성자와 핵심 메서드(<code>equals()</code>, <code>hashCode()</code>, <code>toString()</code>)를 자동으로 생성해준다.</p>
</li>
</ul>
<blockquote>
<p>결론적으로, Record는 불변적이어야 하는 DTO를 위한 완벽한 도구이며, 가변적이어야 하는 Entity와는 역할과 목적이 명확히 구분된다.</p>
<p>따라서, <strong>Entity는 class</strong>로, <strong>DTO는 record</strong>로 구현하는 것이 현대 자바 개발의 모범 사례이다.</p>
</blockquote>
<br>
<br>

<h3 id="record-사용-예제">Record 사용 예제</h3>
<p>기존 class와 Record를 비교하며 Record의 간결함을 살펴보자.</p>
<p>✅ <strong>기존 User 클래스</strong></p>
<pre><code class="language-java">public final class UserDto {
    private final String name;
    private final int age;

    // 1. 모든 필드를 받는 생성자 (정규 생성자)
    public UserDto(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 2. 접근자(getter) 메서드
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // 3. equals() 메서드 오버라이드
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserDto userDto = (UserDto) o;
        return age == userDto.age &amp;&amp; Objects.equals(name, userDto.name);
    }

    // 4. hashCode() 메서드 오버라이드
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    // 5. toString() 메서드 오버라이드
    @Override
    public String toString() {
        return &quot;UserDto[name=&quot; + name + &quot;, age=&quot; + age + &quot;]&quot;;
    }
}</code></pre>
<p>위 코드를 보면, 단순한 데이터 객체임에도 불구하고 상당한 양의 코드를 작성해야 한다.</p>
<br>

<p>✅ <strong>Record를 활용한 User 클래스</strong></p>
<pre><code class="language-java">import java.util.Objects;

// name과 age를 컴포넌트로 가지는 Record 선언
public record UserRecord(String name, int age) {
    // Record는 정규 생성자를 자동으로 생성하지만, 필요시 추가 로직을 넣을 수 있습니다.
    // 예를 들어, 유효성 검사를 추가할 수 있습니다.
    public UserRecord {
        Objects.requireNonNull(name, &quot;이름은 null일 수 없습니다.&quot;);
        if (age &lt; 0) {
            throw new IllegalArgumentException(&quot;나이는 0 미만일 수 없습니다.&quot;);
        }
    }

    // 컴포넌트에 대한 접근자 메서드
    // String name() { return this.name; }
    // int age() { return this.age; }
    // 이 메서드들은 자동으로 생성되므로 직접 작성할 필요가 없습니다.
}</code></pre>
<p>Record는 <strong>헤더(header)에 컴포넌트를 선언</strong>하는 것만으로 모든 핵심 기능을 제공한다.</p>
<blockquote>
<p>💡 <em><strong>Record의 컴팩트 생성자</strong></em></p>
<p><strong>유효성 검사</strong>와 같이 불변 데이터를 초기화하기 전 필요한 추가 로직을 처리하는 데 사용된다.</p>
<p>정규 생성자와 달리 매개변수 목록을 생략하며, 개발자가 직접 필드를 초기화하지 않아도 로직 실행 후 자동으로 초기화가 이루어진다.</p>
</blockquote>
<br>

<p>✅ <strong>Record 인스턴스 생성</strong></p>
<pre><code class="language-java">UserRecord user = new UserRecord(&quot;홍길동&quot;, 20);</code></pre>
<p>Record에서 생성자는 유효성 검사를 위한 수단이며, 실제 객체의 생성과 컴포넌트의 초기화는 헤더에 정의된 컴포넌트를 기반으로 컴파일러가 자동으로 처리한다.</p>
<br>

<h3 id="record의-주요-특징">Record의 주요 특징</h3>
<p><strong>1. 불변성 (Immutability)</strong></p>
<ul>
<li><p>Record는 불변 객체로, 한 번 생성되면 값을 변경할 수 없다.</p>
</li>
<li><p>모든 컴포넌트는 자동으로 <code>private final</code>로 정의되며, <code>setter</code> 메서드가 존재하지 않는다.</p>
</li>
</ul>
<p><strong>2. 간결한 선언</strong></p>
<ul>
<li><p>Record는 암묵적으로 <code>final</code>이므로 상속할 수 없다.(<code>extends</code> 불가)</p>
</li>
<li><p>하지만 인터페이스는 구현할 수 있다.(<code>implements</code> 가능)</p>
</li>
<li><p><code>abstract</code>로 선언할 수 없다.</p>
</li>
</ul>
<p><strong>3. 내부 구조의 제약</strong></p>
<ul>
<li><p>Record는 인스턴스 필드를 추가로 선언할 수 없다. 모든 필드는 헤더에서 정의된 컴포넌트들로만 구성된다.</p>
</li>
<li><p>하지만 <code>static</code> 필드나 <code>static</code> 메서드, 중첩 클래스는 선언할 수 있다.</p>
</li>
</ul>
<p><strong>4. 자동 생성되는 코드</strong></p>
<ul>
<li>Record는 컴포넌트만 정의하면 생성자, <code>getter</code>, <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code> 메서드를 자동으로 제공합니다.</li>
</ul>
<br>
<br>


<p><strong>참고 문서</strong></p>
<ul>
<li><a href="https://velog.io/@pp8817/record">[Java] record에 대하여
</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Spring MVC Flow]]></title>
            <link>https://velog.io/@woomin-wang/Spring-Spring-MVC-Flow</link>
            <guid>https://velog.io/@woomin-wang/Spring-Spring-MVC-Flow</guid>
            <pubDate>Thu, 29 May 2025 05:05:05 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@woomin-wang/Web-Service-Architecture-bkaejdtp">지난 글</a>에서는 웹 서버 아키텍처를 공부했습니다. 이번에는 Spring을 처음 배우면서 새로운 개념들을 접했고, 아직 부족한 점도 많다는 것을 느꼈습니다. 이번 글에서는 기본 개념을 확실히 다지고, 좀 더 깊이 있게 Spring을 이해하는 데 집중하려고 합니다.</p>
<br>

<h2 id="1-spring-application이-실행될-때-무슨-일이">1. Spring Application이 실행될 때 무슨 일이?</h2>
<p>Spring Boot Application을 실행하면, 제일 먼저 눈에 띄는 코드입니다.</p>
<pre><code class="language-java">@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args); // Spring 컨테이너(ApplicationContext) 생성
    }
}</code></pre>
<p>이때 호출되는 <code>SpringApplication.run()</code>은 내부적으로 <strong>Spring Container, 즉 ApplicationContext를 생성합니다.</strong>
이 컨테이너는 애플리케이션 전반에 걸쳐 모든 <strong>Bean</strong>들을 생성하고 관리하는 핵심 역할을 합니다.</p>
<blockquote>
<p>✅ <strong>Bean이란?</strong></p>
</blockquote>
<ul>
<li>Spring 컨테이너가 생성하고 관리하는 객체를 의미하며, 보통 <code>@Component</code>, <code>@Service</code>, <code>@Repository</code>, <code>@Controller</code> 등의 어노테이션이 붙은 클래스가 자동으로 빈으로 등록됩니다.<blockquote>
<ul>
<li>빈은 싱글톤 스코프로 기본 관리되며, 애플리케이션 전반에서 공유되어 사용됩니다.</li>
</ul>
</blockquote>
</li>
</ul>
<br>


<h3 id="spring-container에-대하여">Spring Container에 대하여</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/27851000-4d85-49b2-91fd-cea9f4c0f0d4/image.png" alt=""></p>
<ul>
<li><p><strong><code>@Controller</code>, <code>@Service</code>, <code>@Repository</code></strong> 등이 붙은 클래스들은 <strong>컴포넌트 스캔</strong>을 통해 자동으로 감지되고, Spring Container에 빈으로 등록됩니다.</p>
</li>
<li><p>등록된 빈들은 Spring 컨테이너에 의해 생성, 초기화, 의존성 주입, 소멸까지 생명주기가 관리됩니다.</p>
</li>
</ul>
<br>

<h3 id="dispatcherservlet과-spring-mvc">DispatcherServlet과 Spring MVC</h3>
<p>DispatcherServlet은 Spring MVC의 <strong>Front Controller</strong>로서, 모든 HTTP 요청의 진입점 역할을 합니다.</p>
<ul>
<li><p><strong>DispatcherServlet</strong> 인스턴스는 Spring Container에 의해 하나의 Bean으로 생성 및 관리됩니다.</p>
</li>
<li><p>이 Bean은 서블릿 컨테이너의 서블릿 목록에 &quot;DispatcherServlet 역할을 하는 서블릿&quot;으로 등록됩니다.</p>
</li>
<li><p>이 등록 과정은 <strong>ServletContainerInitializer와 같은 서블릿 컨테이너 초기화 메커니즘</strong>을 통해 자동으로 수행되며, 개발자가 별도로 DispatcherServlet을 등록하지 않아도 웹 애플리케이션 구동 시 자동으로 서블릿 컨테이너에 등록됩니다.</p>
</li>
<li><p>DispatcherServlet은 내부적으로 Spring 컨테이너에서 <code>HandlerMapping</code>, <code>HandlerAdapter</code>, <code>ViewResolver</code> 등 여러 Spring MVC 핵심 컴포넌트들을 주입받아, HTTP 요청 처리 파이프라인을 구성하고 실행합니다.</p>
</li>
</ul>
<br>

<h2 id="2-사용자의-요청이-도착하면">2. 사용자의 요청이 도착하면?</h2>
<p>예를 들어 사용자가 웹 브라우저에서 <code>/members</code> 같은 URL로 요청을 보낸다고 하겠습니다. 
이 요청은 다음과 같은 과정을 통해 처리됩니다.</p>
<ol>
<li><p><strong>요청은 먼저 웹 서버(Web Server)</strong>에 도착합니다. Spring Boot에서는 기본적으로 <strong>내장 톰캣(Tomcat)</strong>을 사용하므로, 별도 설정 없이도 서버가 자동 실행됩니다.</p>
</li>
<li><p>요청이 <strong>정적 리소스</strong>(예: <code>style.css</code>, <code>index.html</code>)에 해당한다면, <code>resources/static</code> 폴더에서 바로 파일을 찾아 응답합니다.</p>
</li>
<li><p>반면에 <strong>동적인 요청</strong>이라면, 웹 서버는 이 요청을 <strong>서블릿 컨테이너</strong>로 넘기고, 컨테이너는 등록된 <strong>DispatcherServlet</strong>에게 전달합니다.</p>
</li>
</ol>
<p>이때부터 Spring MVC의 핵심인 DispatcherServlet이 본격적으로 요청을 처리하기 시작합니다.</p>
<br>

<h2 id="3-어떤-컨트롤러를-호출할까">3. 어떤 컨트롤러를 호출할까?</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/ccdf34d5-b89c-452a-8d37-579b5fd1da54/image.png" alt=""></p>
<p>DispatcherServlet이 모든 요청 처리를 직접 수행하는 것은 아닙니다.
요청이 들어오면 어떤 컨트롤러가 이 요청을 처리할지 결정하는 과정은 다음과 같습니다:</p>
<h3 id="1-handlermapping-">*<em>1. HandlerMapping *</em></h3>
<ul>
<li>요청 URL과 HTTP 메서드를 기준으로, 어떤 컨트롤러의 어떤 메서드(= <strong>HandlerMethod</strong>)가 요청을 처리할지 매핑 정보를 조회합니다.</li>
</ul>
<h3 id="2-handleradapter"><strong>2. HandlerAdapter</strong></h3>
<ul>
<li><p>이렇게 찾은 HandlerMethod는 DispatcherServlet이 직접 실행하지 않고, Spring 컨테이너로부터 주입받은 <strong>HandlerAdapter를 통해 HandlerMethod를 실행합니다.</strong></p>
</li>
<li><p>이때, HandlerAdapter는 요청 파라미터를 알맞은 매개변수에 바인딩한 뒤 컨트롤러 메서드를 실행해줍니다.</p>
</li>
</ul>
<br>

<p>이러한 과정 덕분에 <code>@GetMapping(&quot;/members&quot;)</code> 같은 선언만 해도 복잡한 매핑과 호출, 바인딩 과정이 자동으로 이루어집니다.</p>
<p>또한, HandlerAdapter는 어노테이션 기반, 인터페이스 기반 등 <strong>다양한 형태의 컨트롤러를 일관되게 실행</strong>할 수 있도록 중간에서 조율하는 역할도 합니다.</p>
<blockquote>
<p>💡 이 모든 흐름은 Spring이 실행될 때, <strong>스프링 컨테이너가 DispatcherServlet에 필요한 HandlerMapping과 HandlerAdapter 등을 자동으로 주입해주기 때문에 가능한 일입니다.</strong>
즉, DispatcherServlet을 직접 생성하거나 복잡하게 설정하지 않아도, 요청 처리를 위한 핵심 컴포넌트들이 자동으로 준비되고 작동하게 됩니다.</p>
</blockquote>
<br>

<h2 id="4-본격적인-로직-처리">4. 본격적인 로직 처리</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/858d56d4-035c-460d-9d2a-beaa1d181aa4/image.png" alt=""></p>
<p>HandlerAdapter에 의해 컨트롤러 메서드가 호출되면, 본격적인 애플리케이션 로직이 시작됩니다.</p>
<p>하지만 대부분의 <strong>비즈니스 로직은 컨트롤러에서 직접 처리하지 않고</strong>, 아래와 같은 구조로 위임됩니다.</p>
<h3 id="1-controller"><strong>1. Controller</strong></h3>
<pre><code class="language-java">@RestController
@RequestMapping(&quot;/members&quot;)
public class MemberController {

    private final MemberService memberService;

    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @PostMapping(&quot;/join&quot;)
    public String join(@RequestBody MemberDto memberDto) {
        memberService.join(memberDto);
        return &quot;회원 가입 성공&quot;;
    }
}</code></pre>
<ul>
<li><p>클라이언트로부터 요청을 받고, 요청 데이터를 받아 파라미터로 변환합니다. (<code>@RequestBody</code> 등 사용)</p>
</li>
<li><p>이후 <strong>복잡한 비즈니스 처리는 Service 계층에 위임</strong>하고, 그 결과를 받아 응답으로 반환합니다.</p>
</li>
<li><p>로직이 단순한 경우에는 별도의 서비스 호출 없이 파라미터를 바인딩해 바로 응답을 생성하기도 합니다.</p>
</li>
</ul>
<br>

<h3 id="2-service"><strong>2. Service</strong></h3>
<pre><code class="language-java">@Service
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Transactional
    public void join(MemberDto memberDto) {
        // 도메인 규칙 검사, 트랜잭션 처리 등
        if (memberRepository.existsByEmail(memberDto.getEmail())) {
            throw new IllegalStateException(&quot;이미 존재하는 이메일입니다.&quot;);
        }
        memberRepository.save(new Member(memberDto));
    }
}
</code></pre>
<ul>
<li><p>핵심 비즈니스 로직이 모여있는 곳입니다.</p>
</li>
<li><p>도메인 규칙(예: 이메일 중복 검사), 트랜잭션 관리(한 번에 작업이 완료되거나 실패하도록) 등을 처리합니다.</p>
</li>
<li><p><strong>데이터베이스 작업이 필요하면 Repository에 작업을 위임합니다.</strong></p>
</li>
</ul>
<br>

<h3 id="3-repository"><strong>3. Repository</strong></h3>
<pre><code class="language-java">@Repository
public interface MemberRepository extends JpaRepository&lt;Member, Long&gt; {
    boolean existsByEmail(String email);
}
</code></pre>
<ul>
<li><p><strong>DB와의 실제 통신을 담당</strong>합니다.</p>
</li>
<li><p>JPA, MyBatis, JDBC 등을 통해 DB에 CRUD 작업을 수행합니다.</p>
</li>
</ul>
<br>

<blockquote>
<p>💡 예를 들어, 회원 가입 요청이 들어오면
<code>Controller</code>가 요청을 받아 → <code>Service</code>가 가입 관련 로직을 처리하고 → <code>Repository</code>가 DB에 회원 정보를 저장하는 식입니다.</p>
</blockquote>
<p>이처럼 각 계층의 역할을 분리하면, 코드가 명확해지고 유지보수와 테스트가 훨씬 쉬워집니다.</p>
<br>

<h2 id="5-응답을-어떻게-줄까">5. 응답을 어떻게 줄까?</h2>
<p>컨트롤러가 응답을 반환하면, DispatcherServlet은 이를 받아서 어떻게 클라이언트에게 전달할지 결정합니다.</p>
<p>이때, 반환된 값의 형태에 따라 응답을 만드는 방식이 크게 두 가지로 나뉩니다:</p>
<h3 id="1-뷰view-렌더링"><strong>1. 뷰(View) 렌더링</strong></h3>
<p>컨트롤러가 문자열 형태로 뷰 이름(view name) 을 반환할 때 사용됩니다.</p>
<p>예를 들어, 컨트롤러에서 <code>members/list</code> 라는 뷰 이름을 반환하면, DispatcherServlet은 이 이름을 가지고 <strong>ViewResolver에게 “members/list”라는 템플릿(화면 파일)을 찾아 달라고 요청합니다.</strong></p>
<ul>
<li><p>ViewResolver는 <strong>JSP, Thymeleaf</strong> 같은 뷰 템플릿 엔진과 연결되어 있으며, 실제 템플릿 파일을 찾아서 렌더링합니다.</p>
</li>
<li><p>렌더링이란, 템플릿 파일 안에 포함된 동적 데이터를 HTML 코드로 변환하는 과정입니다.</p>
</li>
<li><p>이렇게 생성된 최종 HTML이 HTTP 응답 본문에 담겨 사용자 브라우저에 전송됩니다.</p>
</li>
</ul>
<p>즉, 뷰 렌더링 방식은 주로 서버에서 HTML 페이지를 만들어서 보내는 전통적인 웹 애플리케이션에 적합합니다.</p>
<h3 id="2json-등-데이터-반환"><strong>2.JSON 등 데이터 반환</strong></h3>
<p>컨트롤러가 자바 객체나 값을 반환할 때, 그리고 그 메서드 위에 <code>@ResponseBody</code>나 <code>@RestController</code> 어노테이션이 붙어 있을 때 사용됩니다.</p>
<p>이 경우, DispatcherServlet은 뷰를 찾지 않고 바로 <strong>HttpMessageConverter라는 컴포넌트를 사용합니다.</strong></p>
<ul>
<li><p>HttpMessageConverter는 자바 객체를 <strong>JSON, XML, 혹은 다른 포맷</strong>으로 자동 변환해 줍니다.</p>
</li>
<li><p>가장 흔하게는 자바 객체를 JSON 문자열로 바꿔서 HTTP 응답 본문에 넣어 보냅니다.</p>
</li>
<li><p>예를 들어, 컨트롤러에서 Member 객체를 반환하면, 이 객체가 JSON 형태로 변환되어 클라이언트에게 전달됩니다.</p>
</li>
<li><p>이는 RESTful API 서버 개발에서 주로 사용되는 방식입니다.</p>
</li>
</ul>
<br>

<h2 id="마무리">마무리</h2>
<p>Spring MVC의 핵심은 DispatcherServlet입니다.
모든 요청을 받아 적절한 컨트롤러로 연결하고, 컨트롤러 실행 후 결과를 뷰나 데이터로 변환해 응답합니다.</p>
<p>이 과정에서 HandlerMapping, HandlerAdapter, ViewResolver, HttpMessageConverter 등이 유기적으로 동작해 개발자가 복잡한 처리 없이 비즈니스 로직에 집중할 수 있게 돕습니다.</p>
<p><strong>DispatcherServlet을 제대로 이해하면 Spring의 전체 흐름과 구조가 명확해집니다.</strong></p>
<br>

<p><strong>참고 문서</strong></p>
<ul>
<li><a href="https://programmer-may.tistory.com/161">스프링 MVC 내부적 흐름 과정</a></li>
<li><a href="https://www.youtube.com/watch?v=hDKoMxhiKSM">[10분 테코톡] 안나의 스프링 MVC와 DispatcherServlet
</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web Service Architecture ]]></title>
            <link>https://velog.io/@woomin-wang/Web-Service-Architecture-bkaejdtp</link>
            <guid>https://velog.io/@woomin-wang/Web-Service-Architecture-bkaejdtp</guid>
            <pubDate>Fri, 16 May 2025 08:29:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@woomin-wang/Java-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EC%8B%9D-JAR%EC%99%80-WAR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-Spring-Boot">지난 글</a>에서 JAR, WAR 파일에 대해 학습하던 중 Web Service Architecture 관련 개념에서 어려움을 겪었습니다. 이에 이번 글에서는 이러한 난항을 극복하고자 <strong>Web Service Architecture의 전반적인 구조와 구성 요소를 깊이 있게 학습하고자 합니다.</strong> 이를 통해 웹 서비스의 동작 원리를 보다 명확히 이해하고, 실제 개발 과정에서의 활용 능력을 향상시키는 것을 목표로 합니다.</p>
<br>

<h2 id="정적-페이지static-pages-동적-페이지dynamic-pages란">정적 페이지(Static Pages), 동적 페이지(Dynamic Pages)란?</h2>
<p>웹 서비스를 이해하기 위해서는 먼저 정적 페이지, 동적 페이지의 개념을 이해할 필요가 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/00cccd78-e12a-4e36-811f-1bcdce885688/image.png" alt=""></p>
<ul>
<li><p><strong>정적 페이지(Static Pages):</strong> 내용이 고정되어 있고, 사용자 요청과 무관하게 항상 같은 결과를 제공하는 페이지입니다.
Ex) HTML, CSS, JavaScript, 이미지 등</p>
<ul>
<li><strong>동적 페이지(Dynamic Pages):</strong> 사용자의 요청이나 입력에 따라 그때그때 다른 결과를 생성하여 제공하는 페이지입니다.
Ex) 게시판 글 보기, 로그인 처리, 검색 결과 페이지 등</li>
</ul>
</li>
</ul>
<br>

<h2 id="web-server는-정적-콘텐츠-처리-전문가">Web Server는 정적 콘텐츠 처리 전문가</h2>
<p><strong>Web Server는 정적인 콘텐츠 처리에 특화된 서버로, HTML, 이미지, CSS와 같은 파일을 빠르고 효율적으로 제공하는 데 적합합니다.</strong></p>
<p>반면, 사용자 요청에 따라 내용이 달라지는 동적 콘텐츠의 경우, Web Server가 직접 처리하기에는 구조적으로 비효율적입니다. 따라서 이러한 요청은 WAS(Web Application Server)에게 위임하여 처리합니다.</p>
<h3 id="web-server는-왜-동적-콘텐츠를-처리하지-않을까">Web Server는 왜 동적 콘텐츠를 처리하지 않을까?</h3>
<p>과거에는 Web Server가 CGI(Common Gateway Interface) 방식을 통해 동적 콘텐츠를 처리했습니다. 하지만 <strong>CGI는 요청마다 새로운 프로세스를 생성하기 때문에</strong>, 리소스 소모가 크고 처리 속도가 느려 성능과 확장성에 한계가 있습니다.</p>
<p>이러한 문제를 해결하기 위해, 현대에는 WAS + Web Container 구조가 표준처럼 사용됩니다. 이 구조는 프로세스를 생성하지 않고 스레드 기반으로 요청을 처리하므로, 훨씬 더 가볍고 빠르며 많은 요청을 효율적으로 처리할 수 있습니다.</p>
<br>

<h2 id="web-server란">Web Server란?</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/50c36a90-6bdc-4826-8edb-71708adc9988/image.png" alt=""></p>
<p><strong>Web Server는 웹 브라우저(클라이언트)로부터 HTTP 요청을 받아, 정적 콘텐츠를 처리하여 응답하는 서버 프로그램입니다.</strong></p>
<p>예를 들어 사용자가 웹 페이지를 요청하면, Web Server는 미리 저장된 HTML, 이미지 등의 파일을 찾아 그대로 전달합니다. 
대표적인 Web Server로는 <strong>Apache HTTP Server, Nginx, Microsoft IIS</strong> 등이 있습니다.</p>
<p>Web Server는 <strong>정적 콘텐츠는 직접 처리</strong>하고, <strong>동적 콘텐츠 요청은 WAS로 전달</strong>한 뒤, WAS가 생성한 결과를 클라이언트에게 다시 전달하는 중계 역할을 수행합니다.</p>
<h2 id="was와-web-container의-관계">WAS와 Web Container의 관계</h2>
<p>앞서 살펴본 것처럼, Web Server는 정적 콘텐츠만 직접 처리하며, 동적 콘텐츠에 대한 요청은 WAS로 전달한다고 설명했습니다. 하지만 보다 정확히 말하자면, <strong>동적 콘텐츠의 실제 처리는 WAS 내부의 Web Container(웹 컨테이너)가 담당합니다.</strong></p>
<p>Web Container는 WAS 내부에 포함된 핵심 구성요소 중 하나로, <strong>Servlet, JSP와 같은 동적 웹 기술을 실행하는 환경을 제공합니다.</strong></p>
<blockquote>
<p> <strong>Servlet:</strong> 자바 언어로 작성된 서버용 프로그램으로, 클라이언트의 요청을 받아 처리하고 그 결과를 만들어 내는 역할을 합니다.</p>
<p><strong>JSP(JavaServer Pages):</strong> HTML 코드 안에 자바 코드를 삽입하여 동적으로 웹 페이지를 생성할 수 있게 해주는 기술입니다.</p>
</blockquote>
<ul>
<li><p>클라이언트로부터 요청이 들어오면, Web Container는 해당 요청에 맞는 Servlet이나 JSP를 실행합니다.</p>
</li>
<li><p>내부적으로 비즈니스 로직을 수행하고, <strong>결과를 HTML과 같은 정적 형태의 콘텐츠로 변환합니다.</strong> (웹 브라우저는 정적인 콘텐츠만 해석 가능)</p>
</li>
<li><p>변환된 응답은 Web Server를 거쳐 클라이언트(웹 브라우저)로 전달됩니다.</p>
</li>
</ul>
<h3 id="그렇다면-was는-무엇을-할까">그렇다면 WAS는 무엇을 할까?</h3>
<p>WAS는 단순히 Web Container만 제공하는 것이 아니라, 웹 애플리케이션을 실행하는 데 필요한 다양한 기능을 통합적으로 제공합니다.</p>
<ul>
<li><p><strong>Web Container 포함 (Servlet/JSP 실행)</strong></p>
</li>
<li><p><strong>보안 기능(인증, 권한 등)</strong></p>
</li>
<li><p><strong>세션 및 상태 관리</strong></p>
</li>
<li><p><strong>트랜잭션 처리</strong></p>
</li>
<li><p><strong>데이터베이스 연결 및 관리</strong></p>
</li>
</ul>
<p>이처럼 WAS는 복잡한 비즈니스 로직을 안정적으로 처리하고, 웹 서비스를 원활하게 운영할 수 있도록 다양한 기능을 함께 제공합니다.</p>
<h2 id="tomcat은-web-server인가-was인가">Tomcat은 Web Server인가? WAS인가?</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/24f0d5cf-ac71-44cb-816c-5b305d735a46/image.png" alt=""></p>
<p><strong>원래 개념상 WAS는 내부에 Web Container를 포함하는 구조이고, Web Server는 별도의 독립된 구성요소입니다.</strong></p>
<p>하지만 대표적인 <strong>WAS인 Tomcat은 정적 콘텐츠 요청을 처리할 수 있는 Web Server 기능도 내장하고 있습니다.</strong> 즉, Apache HTTP Server 같은 별도의 Web Server 없이도 정적 콘텐츠(HTML, 이미지 등)를 직접 응답할 수 있다는 뜻입니다.</p>
<h3 id="web-serverapache-http-server를-사용하는-이유는">Web Server(Apache HTTP Server)를 사용하는 이유는?</h3>
<p>그렇다면, Tomcat이 Web Server 기능까지 내장하고 있는데도 Apache HTTP Server 와 연동하는 경우가 존재하는 이유는 무엇일까요?</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/cdb812cd-da69-48a6-91e6-0667fca0f815/image.png" alt=""></p>
<p><strong>1. 성능 최적화</strong></p>
<ul>
<li><p>Apache HTTP Server는 오랜 시간 동안 웹 서버 역할에 집중해서 개발되어 왔기 때문에, 정적인 파일을 빠르고 효율적으로 처리하는 데 최적화되어 있습니다.</p>
</li>
<li><p>반면 Tomcat은 주로 동적 콘텐츠를 처리하는 데 초점이 맞춰져 있기 때문에, 정적 콘텐츠 처리에서는 Apache만큼 빠르지 않을 수 있습니다.</p>
</li>
<li><p>그래서 보통 정적인 요청은 Apache가 빠르게 처리하고, 동적인 요청만 Tomcat에 넘겨주는 구조를 사용합니다. 이 방식은 각 서버가 잘하는 역할에 집중하게 해 전체적인 처리 속도를 높이고 서버 자원을 효율적으로 사용할 수 있게 해줍니다.</p>
</li>
</ul>
<p><strong>2. 로드 밸런싱</strong></p>
<ul>
<li><p>웹 서비스가 커지고 사용자 수가 많아지면, 하나의 Tomcat 서버만으로는 모든 요청을 처리하기 어려워집니다.</p>
</li>
<li><p>Apache HTTP Server는 여러 대의 Tomcat 서버에 요청을 분산시키는 ‘로드 밸런싱’ 기능을 지원합니다.</p>
</li>
<li><p>이를 통해 트래픽을 균등하게 나누어 서버 과부하를 방지하고, 한 서버가 다운되어도 다른 서버가 서비스를 계속 제공하게 만들어 시스템의 안정성과 확장성을 높일 수 있습니다.</p>
</li>
</ul>
<p><strong>3. 보안성 향상</strong></p>
<ul>
<li><p>Apache HTTP Server는 다양한 보안 모듈(예: mod_security)을 통해 세밀한 접근 제어나 방화벽 역할을 수행할 수 있습니다.</p>
</li>
<li><p>SSL 인증서 설정, IP 차단, 요청 필터링 등도 Apache에서 더 쉽게, 그리고 강력하게 구현할 수 있습니다.</p>
</li>
<li><p>Tomcat 서버를 직접 외부에 노출시키지 않고, Apache가 앞단에서 보안 계층 역할을 하면서 Tomcat을 보호하는 구조가 많이 사용됩니다. 이렇게 하면 보안 위험을 줄이고, 공격으로부터 시스템을 더 안전하게 지킬 수 있습니다.</p>
</li>
</ul>
<p><strong>4. 캐싱</strong></p>
<ul>
<li><p>Apache HTTP Server는 정적 리소스에 대해 캐싱 기능을 제공해, 같은 파일을 여러 번 요청할 때 빠르게 응답할 수 있습니다.</p>
</li>
<li><p>캐싱 덕분에 서버가 매번 같은 파일을 디스크에서 읽지 않고도 빠르게 전달할 수 있어서 서버 부하가 줄고 사용자 경험도 개선됩니다.</p>
</li>
<li><p>Tomcat은 이런 캐싱에 특화된 기능이 적기 때문에, 정적 자원은 Apache가 캐싱해서 처리하는 것이 효율적입니다.</p>
</li>
</ul>
<br>

<h2 id="web-service-architecture의-구조와-흐름">Web Service Architecture의 구조와 흐름</h2>
<p>이번에는 클라이언트가 웹 요청을 보낸 뒤, Apache HTTP Server와 Tomcat이 어떻게 협력하여 요청을 처리하고 응답을 전달하는지 그 구조와 흐름에 대해 알아보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/3b0d6f9d-aaf4-466c-96ce-be55ca06aa96/image.png" alt=""></p>
<p><strong>1. 클라이언트 요청 (웹 브라우저)</strong></p>
<ul>
<li>클라이언트가 요청을 보냅니다. 예를 들어, <a href="http://example.com/index.html">http://example.com/index.html</a> 같은 URL을 요청한다고 가정해보겠습니다.</li>
</ul>
<p><strong>2. Apache HTTP Server</strong></p>
<ul>
<li>클라이언트의 요청이 먼저 Apache HTTP Server에 전달됩니다. Apache는 정적 콘텐츠에 대해 매우 빠르게 응답할 수 있기 때문에, 정적 파일에 대한 요청은 Apache가 처리합니다. Apache는 index.html과 같은 파일을 바로 클라이언트에 전달합니다.</li>
</ul>
<p><strong>3. Tomcat으로 전달 (동적 요청)</strong></p>
<ul>
<li>만약 요청이 동적 콘텐츠에 관련된 것이라면, Apache는 해당 요청을 Tomcat으로 전달합니다. Apache는 Tomcat으로 프록시 요청을 보냅니다. 이때 mod_proxy나 mod_jk와 같은 모듈이 Apache와 Tomcat 간의 연결을 담당합니다.Tomcat은 Web Container로서, 해당 요청을 Servlet/JSP로 처리하고 응답을 생성합니다.</li>
</ul>
<p><strong>4. Tomcat 응답 처리</strong></p>
<ul>
<li>Tomcat은 동적 콘텐츠 처리 후, 결과를 Apache HTTP Server로 전달합니다.</li>
</ul>
<p><strong>5. 최종 응답</strong></p>
<ul>
<li>Apache는 Tomcat에서 받은 응답을 클라이언트에게 최종적으로 전달합니다.</li>
</ul>
<br>

<h2 id="프록시란">프록시란?</h2>
<p>앞서 설명한 &quot;Apache는 Tomcat으로 프록시 요청을 보냅니다.&quot; 여기서 프록시란 무엇일까요?</p>
<p>프록시(Proxy)는 말 그대로 대리인이라는 뜻으로, *<em>클라이언트와 서버 사이에 위치하여 요청이나 응답을 중계하는 중간 서버를 의미합니다.
*</em></p>
<h3 id="프록시의-종류">프록시의 종류</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/5da16a64-6cc5-4580-9868-0bdbef5255ea/image.png" alt=""></p>
<p><strong>정방향 프록시 (Forward Proxy)</strong></p>
<ul>
<li>클라이언트가 외부 서버에 직접 접근하지 않고, 프록시 서버를 거쳐 요청을 보냅니다.</li>
<li>외부 서버는 실제 클라이언트를 알 수 없고, 프록시 서버만 인식합니다.</li>
<li>Ex) 회사 내부망에서 인터넷 접속 시 사용하는 프록시 서버.</li>
</ul>
<p>*<em>역방향 프록시(Reverse Proxy) *</em> </p>
<ul>
<li>클라이언트는 프록시 서버만 알고, 실제 응답을 처리하는 서버(Tomcat 등)는 숨겨집니다.</li>
<li>Apache가 Tomcat 앞단에서 프록시 역할 수행하는 방식이 여기에 해당합니다.</li>
<li>클라이언트는 오직 Apache만 접촉하고, Apache가 요청을 내부 서버(Tomcat 등)로 전달합니다.</li>
</ul>
<br>

<h2 id="apache와-tomcat-연결하는-프록시-모듈">Apache와 Tomcat 연결하는 프록시 모듈</h2>
<p>Apache가 Tomcat과 연동되기 위해서는 요청을 <strong>Tomcat으로 전달하는 프록시 모듈</strong>이 필요합니다. 
다음은 대표적인 프록시 방식입니다.</p>
<p><strong>mod_jk (AJP 기반)</strong></p>
<ul>
<li>AJP(Apache JServ Protocol) 기반 통신을 지원하는 모듈</li>
<li>AJP는 이진(binary) 프로토콜로 HTTP보다 데이터 전송 속도가 빠르다.</li>
<li>설정이 복잡하고 보안 취약점(Ghostcat 등)이 지적되면서 최근에는 사용이 줄어드는 추세입니다.</li>
</ul>
<p><strong>mod_proxy (HTTP 기반)</strong></p>
<ul>
<li>일반적인 HTTP 기반 프록시 서버처럼 동작하도록 해주는 모듈 </li>
<li><strong>mod_proxy_http</strong>와 함께 사용하면 Apache가 받은 HTTP 요청을 Tomcat으로 간편하게 전달할 수 있습니다. </li>
<li>설정이 단순하고 다양한 프록시 시나리오(로드 밸런싱, 캐싱 등)를 유연하게 구성할 수 있어 현대적인 웹 시스템에서 더 많이 활용됩니다.</li>
</ul>
<br>

<p>Apache와 Tomcat을 함께 사용할 때의 여러 이점은 사실 프록시 구조 덕분에 가능합니다. 
<strong>Apache는 클라이언트의 요청을 중간에서 받아 처리하는 프록시 서버 역할을 하며,</strong> 이 기능은 mod_proxy 같은 프록시 모듈을 통해 구현됩니다.</p>
<p>결과적으로 각 서버의 특성을 최대한 활용한 Apache와 Tomcat의 조합은 성능 최적화, 보안 강화, 확장성 향상 등의 장점을 갖춘, 강력하고 유연한 웹 서비스 아키텍처를 만들어냅니다.</p>
<br>
<br>

<p><strong>참고 문서</strong></p>
<ul>
<li><a href="https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html">Web Server와 WAS의 차이와 웹 서비스 구조</a></li>
<li><a href="https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC">웹 서비스 구조 (Web서버 / 웹컨테이너 / WAS) 정리</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 웹 애플리케이션 배포 방식: JAR와 WAR의 차이점과 Spring Boot]]></title>
            <link>https://velog.io/@woomin-wang/Java-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EC%8B%9D-JAR%EC%99%80-WAR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-Spring-Boot</link>
            <guid>https://velog.io/@woomin-wang/Java-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EC%8B%9D-JAR%EC%99%80-WAR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-Spring-Boot</guid>
            <pubDate>Tue, 13 May 2025 10:36:03 GMT</pubDate>
            <description><![CDATA[<h2 id="jar은-왜-생겼을까">JAR은 왜 생겼을까?</h2>
<p>Java는 JVM만 있으면 OS에 관계없이 .class 파일을 실행할 수있는 독립적인 언어입니다. 하지만 현실에서는 하나의 프로그램이 여러 개의 .class, 설정 파일, 라이브러리로 구성되며, 이를 매번 따로 관리하기엔 너무 번거롭습니다.</p>
<p>그래서 등장한 것이 <strong>JAR(Java ARchive)</strong>입니다.
JAR은 관련된 파일들을 하나의 압축 파일로 묶어, 다음처럼 실행할 수 있게 해줍니다.</p>
<pre><code>java -jar myapp.jar</code></pre><p>이렇게 실행하려면, 내부의 <code>MANIFEST.MF</code> 파일에 <code>Main-Class</code>가 지정되어 있어야 합니다.
<strong>JAR은 일반 Java 애플리케이션을 쉽게 배포하고 실행하기 위해 만든 포맷</strong>입니다.</p>
<br>

<h2 id="was와-war의-관계-웹-요청이-처리되는-구조">WAS와 WAR의 관계: 웹 요청이 처리되는 구조</h2>
<p>하지만 웹 애플리케이션은 콘솔 프로그램처럼 단순히 main() 함수에서 시작해 끝나는 구조가 아닙니다. <strong>웹 애플리케이션은 사용자의 요청이 있어야 비로소 동작을 시작하는 이벤트 기반 구조</strong>입니다.</p>
<p>이때 사용자의 요청을 받아들이는 것이 바로 <strong>WAS(Web Application Server)</strong>이며, 대표적으로는 <strong>Tomcat</strong>, <strong>Jetty</strong> 등이 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/d0538831-4682-4266-b2cd-8f61632ce78d/image.png" alt=""></p>
<p>클라이언트가 브라우저를 통해 HTTP 요청을 보내면, 이 요청은 먼저 <strong>WAS(Web Application Server)</strong>에 도달합니다. WAS는 내부의 <strong>Servlet Container</strong>에게 요청을 전달하고, <strong>Servlet Container</strong>는 <strong><code>web.xml</code></strong> 설정 파일이나 애노테이션 정보를 참고해 해당 요청에 매핑된 <strong>Servlet 클래스</strong>를 찾아 실행합니다. 이렇게 실행된 <strong>Servlet</strong>은 비즈니스 로직을 처리한 후, 응답 데이터를 생성하여 WAS를 통해 다시 클라이언트에게 전달합니다.</p>
<p>이 전체 과정의 핵심에는 <strong>WAR(Web Application Archive)</strong> 파일이 있습니다. <strong>WAR는 하나의 완성된 웹 애플리케이션 배포 단위</strong>로, HTTL, CSS, JS, 이미지, .class 파일, 라이브러리, JAR, 설정 파일 등을 포함한 압축 파일입니다. 이 WAR 파일은 개발자가 작성하지만, 실제로는 <strong>WAS가 설치된 서버의 지정된 위치</strong>에 배포되어 <strong>WAS가 소유하고 관리</strong>하게 됩니다.</p>
<p>서버가 시작되면 WAS는 WAR 파일을 탐지하고 내부적으로 압축을 해제해 실행 가능한 형태로 준비합니다. 이후 <strong>Servlet Container</strong>가 이 구조를 기반으로 HTTP 요청을 처리합니다. 즉, <strong>WAR는 WAS와 Servlet Container가 웹 애플리케이션을 실행하기 위해 반드시 필요한 표준화된 구조</strong>이며, 배포 이후에는 WAS의 통제 하에 동작하는 실행 단위라고 볼 수 있습니다. </p>
<br>

<h3 id="✅-war-내부-구조-예시">✅ <strong>WAR 내부 구조 예시</strong></h3>
<pre><code>mywebapp.war
├── index.html
├── images/
├── css/
└── WEB-INF/
    ├── web.xml         ← 서블릿 설정 파일
    ├── classes/        ← 컴파일된 서블릿(.class)
    └── lib/            ← 외부 라이브러리(JAR)
</code></pre><p><strong><code>WEB-INF</code></strong>는 <strong>웹 애플리케이션의 보안 영역</strong>으로, 외부(클라이언트)에서는 직접 접근할 수 없습니다. 이 폴더는 오직 <strong>WAS 내부의 Servlet Container만 접근</strong>할 수 있도록 설계되어 있으며, 애플리케이션의 동작에 필수적인 설정 파일, 클래스 파일, 외부 라이브러리 등을 담고 있습니다.</p>
<p>그 외의 index.html, css/, images/ 같은 정적 자원들은 클라이언트가 브라우저를 통해 직접 요청하고 확인할 수 있는 영역입니다.</p>
<br>

<h3 id="✅-jar와-war의-차이와-용도-정리">✅ <strong>JAR와 WAR의 차이와 용도 정리</strong></h3>
<blockquote>
</blockquote>
<p><em><strong>JAR (Java ARchive)</strong></em></p>
<ul>
<li>Java 클래스 파일과 리소스를 하나로 묶은 압축 포맷</li>
<li>주로 <strong>라이브러리나 실행 가능한 Java 애플리케이션</strong> 형태로 사용됨<blockquote>
</blockquote>
<em><strong>WAR (Web Application Archive)</strong></em></li>
<li>웹 애플리케이션을 구성하는 모든 파일을 묶은 압축 포맷</li>
<li>내부에는 <strong>HTML, CSS, JS, 이미지, 클래스 파일, JAR 파일</strong> 등 웹 애플리케이션 실행에 필요한 모든 리소스 포함</li>
<li>JAR 파일을 포함할 수 있는 <strong>웹 애플리케이션 배포 포맷</strong></li>
</ul>
<br>

<h2 id="spring-boot는-어떻게-jar을-사용할까">Spring Boot는 어떻게 JAR을 사용할까?</h2>
<p>기존에는 Java 웹 애플리케이션을 만들면 반드시 WAR 파일을 생성하여 Tomcat과 같은 웹 애플리케이션 서버에 배포해야 했습니다. 이 과정은 꽤 번거롭고 설정이 많았습니다. <strong>Tomcat 서버를 설치하고, 설정 파일을 수정하며, WAR 파일을 배포하는 등 여러 단계를 거쳐야 했습니다.</strong></p>
<p>이 번거로움을 해결하기 위해 등장한 것이 바로 <strong>Spring Boot</strong>입니다.
Spring Boot는 <strong>내장 Tomcat</strong>을 제공하여 서버 설정과 배포 과정을 단순화했습니다. Spring Boot는 <strong>JAR 포맷</strong>을 사용하여 웹 애플리케이션을 실행 가능한 독립형 애플리케이션으로 만들 수 있게 해줍니다.</p>
<p>Spring Boot의 <strong>JAR 파일은 기존의 WAR 파일처럼 서버에 배포할 필요 없이, 애플리케이션 코드 (Controller, Service 등), HTML/JSP 리소스, 의존성 라이브러리, 내장 Tomcat 서버를 모두 포함</strong>하여, 단일 JAR 파일로 애플리케이션을 실행할 수 있도록 했습니다.</p>
<br>

<h2 id="마무리">마무리</h2>
<p>JAR과 WAR의 차이점을 알아보면서, JAR 파일은 독립 실행형 애플리케이션을 간편하게 배포하고 실행할 수 있는 포맷인 반면, WAR 파일은 웹 서버에 배포되는 웹 애플리케이션 포맷이라는 점에서 차이가 있음을 알게 되었습니다. 그 과정에서 웹 애플리케이션 서버(WAS)의 동작 방식과 Spring Boot가 JAR 파일 방식을 사용한다는 점도 배우게 되어 흥미로웠습니다. 관련 내용들은 앞으로 Spring을 학습하면서 더 깊이 탐구할 계획입니다.</p>
<br>

<p><strong>참고 문서</strong></p>
<ul>
<li><a href="https://cocococo.tistory.com/entry/JAR-WAR-%EB%B0%B0%ED%8F%AC-%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-%EC%9E%A5%EB%8B%A8%EC%A0%90">JAR WAR 배포 차이점과 장단점</a></li>
<li><a href="https://velog.io/@mooh2jj/JAR-vs-WAR-%EB%B0%B0%ED%8F%AC%EC%9D%98-%EC%B0%A8%EC%9D%B4">JAR vs WAR 배포의 차이</a></li>
<li><a href="https://loosie.tistory.com/722">자바 서블릿과 서블릿 컨테이너</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] Merge & Rebase]]></title>
            <link>https://velog.io/@woomin-wang/Git-merge-rebase</link>
            <guid>https://velog.io/@woomin-wang/Git-merge-rebase</guid>
            <pubDate>Fri, 09 May 2025 09:17:26 GMT</pubDate>
            <description><![CDATA[<p>WiSoft Git 세미나에서 <strong>Merge</strong>와 <strong>Rebase</strong>에 대해 학습하였습니다.</p>
<p>우선 Git-visualizing 도구를 활용해 merge와 rebase 명령어를 실행하며, 각 명령어에 따른 커밋 이력의 구조 변화를 시각적으로 확인했습니다. 이후에는 VS Code를 사용해 실제 상황을 구성하고, 직접 명령어를 실행해보며 실습을 진행하였습니다.</p>
<p>Git-visualizing 도구에서 사용된 Master 브랜치는 Main 브랜치로 명명하여 이해를 통일했습니다.</p>
<p>처음에 Merge와 Rebase의 주체와 대상 개념이 혼동되어 이를 먼저 정리한 뒤 학습을 이어갔습니다.</p>
<h3 id="git-merge-vs-rebase-주체--대상-정리">Git merge vs rebase: 주체 / 대상 정리</h3>
<ul>
<li><strong>merge</strong>: 현재 브랜치(주체)에 <strong>다른 브랜치(대상)</strong>의 변경사항을 <strong>끌어온다.</strong></li>
<li><strong>rebase</strong>: 현재 브랜치(주체)를 <strong>다른 브랜치(대상)</strong>의 최신 커밋 위에 <strong>다시 쌓는다.</strong></li>
</ul>
<br>

<table>
<thead>
<tr>
<th>구분</th>
<th>명령어</th>
<th>주체 (현재 브랜치)</th>
<th>대상 (인수 브랜치)</th>
<th>결과 요약</th>
</tr>
</thead>
<tbody><tr>
<td>merge</td>
<td><code>git checkout main</code><br><code>git merge feature</code></td>
<td><code>main</code></td>
<td><code>feature</code></td>
<td><code>main</code> 브랜치에 <code>feature</code>의 변경사항이 병합됨</td>
</tr>
<tr>
<td>rebase</td>
<td><code>git checkout feature</code><br><code>git rebase main</code></td>
<td><code>feature</code></td>
<td><code>main</code></td>
<td><code>feature</code> 브랜치를 <code>main</code> 뒤에 다시 쌓음</td>
</tr>
</tbody></table>
<br>

<h2 id="merge">Merge</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/cc3eb3ed-b537-47fd-a593-59733e716d54/image.png" alt="">
✔️ <strong>입력한 명령어</strong></p>
<pre><code class="language-shell">git checkout master
git merge feature</code></pre>
<p><strong><code>main</code></strong> 브랜치로 <strong><code>feature</code></strong> 브랜치를 merge하면, 결과적으로 <strong>새로운 병합 커밋</strong>이 생성됩니다. 즉, <strong><code>main</code></strong> 브랜치(주체)가 <strong><code>feature</code></strong> 브랜치(대상)의 변경사항을 <strong>끌어와 자신의 이력에 통합하는 방식</strong>입니다.</p>
<h2 id="rebase">Rebase</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/6881a77e-99e1-430a-ab47-6660c0cb58f8/image.png" alt="">
✔️ <strong>입력한 명령어</strong></p>
<pre><code class="language-shell">git checkout feature
git rebase master</code></pre>
<p><strong><code>feature</code></strong> 브랜치를 <strong><code>main</code></strong> 브랜치를 기준(base)으로 다시 커밋을 쌓습니다. 즉, <strong><code>feature</code></strong> 브랜치(주체)가 <strong><code>main</code></strong> 브랜치(대상) 위로 올라가는 구조입니다.</p>
<p><strong>rebase</strong>는 기존 커밋을 재사용하지 않고, <strong>새로운 커밋 객체를 생성</strong>합니다. 이때 생성되는 커밋들은 기존 커밋과 동일한 내용이라 하더라도 <strong>해시값이 달라지며</strong>, 기존 <strong><code>feature</code></strong> 브랜치의 커밋 이력은 사라지고 <strong>새로운 이력으로 대체</strong>됩니다.</p>
<h2 id="브랜치-이력-맞추기">브랜치 이력 맞추기</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/95bec220-a3dc-4b7b-8a9e-d213ad7f77ec/image.png" alt=""></p>
<p>✔️ <strong>입력한 명령어</strong></p>
<pre><code class="language-shell">git checkout master
git merge feature</code></pre>
<p>이후 <strong><code>main</code></strong> 브랜치에서 <strong><code>git merge feature</code></strong>를 실행하면, 두 브랜치가 선형 관계일 경우 Git은 <strong>fast-forward</strong> 방식으로 병합을 수행합니다.
이는 <strong>새로운 병합 커밋 없이</strong>, <strong><code>main</code></strong> 브랜치의 포인터를 <strong><code>feature</code></strong> 브랜치의 최신 커밋으로 단순히 이동시키는 방식입니다.</p>
<h3 id="✅-fast-forward란">✅ Fast-forward란?</h3>
<blockquote>
<p><strong>fast-forward</strong>는 브랜치 간 병합 시, <strong>현재 브랜치의 커밋 이력이 대상 브랜치의 이력에 포함되어 있을 경우</strong>, 즉 두 브랜치가 <strong>선형 관계</strong>일 때, <strong>새로운 병합 커밋 없이 포인터만 이동</strong>시키는 방식입니다.</p>
</blockquote>
<br>

<h2 id="vs-code-실습">VS-Code: 실습</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/7e0853e7-66b7-4a4e-bbbb-1e8aaa9f7825/image.png" alt=""></p>
<p>✔️ <strong>입력한 명령어</strong></p>
<pre><code class="language-shell"># main 브랜치의 상태
echo &quot;console.log(&#39;Hello from main&#39;);&quot; &gt; index.js
git add index.js
git commit -m &quot;feat: 초기 main 커밋&quot;

# feature 브랜치 생성 및 커밋
git switch -c feature

echo &quot;console.log(&#39;Feature A&#39;);&quot; &gt;&gt; index.js
git add index.js
git commit -m &quot;feat: Feature A 추가&quot;

echo &quot;console.log(&#39;Feature B&#39;);&quot; &gt;&gt; index.js
git add index.js
git commit -m &quot;feat: Feature B 추가&quot;

# main 브랜치에서 추가 작업
git switch main

echo &quot;console.log(&#39;Hotfix from main&#39;);&quot; &gt;&gt; index.js
git add index.js
git commit -m &quot;fix: main 브랜치에서 hotfix&quot;</code></pre>
<p>현재 저장소에는 <strong><code>main</code></strong>과 <strong><code>feature</code></strong> 두 개의 브랜치가 존재합니다. <strong><code>main</code></strong> 브랜치와 <strong><code>feature</code></strong> 브랜치가 <strong>서로 다른 커밋 이력</strong>을 가지며, <strong><code>feature</code></strong> 브랜치는 기능 개발 커밋 2개를, <strong><code>main</code></strong> 브랜치는 핫픽스 커밋 1개를 포함하고 있는 상태입니다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/a13d2dec-feb4-43c4-90ea-15a934c3de7a/image.png" alt=""></p>
<p><strong><code>feature</code></strong> 브랜치에서 <strong><code>main</code></strong> 브랜치를 기준으로 <strong>rebase</strong> 했습니다.
앞서 살펴본 대로, 기존 커밋들이 새로운 해시값을 가진 커밋으로 대체되었으며, <strong><code>feature</code></strong> 브랜치의 기존 커밋 이력은 사라진 것을 확인할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/99829eba-3aa2-47a9-bc39-b36e1726a4b4/image.png" alt=""></p>
<p><strong><code>main</code></strong> 브랜치로 switch한 후, <strong><code>feature</code></strong> 브랜치를 merge 했습니다. 이미 <strong><code>feature</code></strong> 브랜치에 <strong><code>main</code></strong> 브랜치의 이력이 포함되어 있어, 선형적 구조가 되어 <strong>fast-forward</strong> 방식으로 병합이 이루어졌습니다. 결과적으로 main 브랜치의 포인터만 변경된 걸 확인할 수 있습니다.</p>
<h2 id="rebase-충돌-해결-과정">Rebase 충돌 해결 과정</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/e7bfffa1-2a27-473c-a935-25a4d5953ac9/image.png" alt=""></p>
<p><strong>Rebase</strong>는 <strong>Merge</strong>에 비해 충돌 발생 가능성이 더 높습니다.
위 상황에서는 <code>Feature A 추가</code>, <code>Feature B 추가</code> 두 커밋이 각각 <strong><code>main</code></strong> 브랜치와 충돌할 경우, 각 커밋마다 충돌을 해결해야 합니다. rebase 과정에서는 커밋을 다시 적용하면서 여러 단계에서 충돌이 발생할 수 있으며, 각 충돌을 수동으로 해결한 후 <strong><code>git rebase --continue</code></strong>를 통해 계속 진행해야 합니다.</p>
<br>


<p><strong>참고사항</strong></p>
<ul>
<li><p><a href="https://dongminyoon.tistory.com/9">[GIT] Merge vs Rebase 차이</a></p>
</li>
<li><p><a href="https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%ec%99%80-Merge-%ec%9d%98-%ea%b8%b0%ec%b4%88">Git 브랜치 - 브랜치와 Merge 의 기초</a></p>
</li>
<li><p><a href="https://git-school.github.io/visualizing-git">visualizing-git</a> </p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] Git의 동작원리: 스냅샷, Commit, Branch]]></title>
            <link>https://velog.io/@woomin-wang/Git-Git-%EC%A0%80%EC%9E%A5%EC%86%8C-%EA%B5%AC%EC%A1%B0%EC%99%80-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91</link>
            <guid>https://velog.io/@woomin-wang/Git-Git-%EC%A0%80%EC%9E%A5%EC%86%8C-%EA%B5%AC%EC%A1%B0%EC%99%80-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91</guid>
            <pubDate>Mon, 05 May 2025 07:33:25 GMT</pubDate>
            <description><![CDATA[<h1 id="git은-어떻게-동작할까">Git은 어떻게 동작할까?</h1>
<p>Git은 자동으로 변경된 파일을 추적하며, 자유자재로 브랜치를 이동하고 이전 상태로 되돌릴 수 있다. 
도대체 어떻게 이런 기능이 가능한 걸까?</p>
<br>

<h2 id="git은-스냅샷을-저장한다">Git은 &#39;스냅샷&#39;을 저장한다.</h2>
<p>Git은 파일 변경의 차이(diff)만 저장하는 게 아니라, <strong>전체 폴더 구조(Tree)</strong>와 <strong>파일 내용(Blob)</strong>의 상태를 통째로 저장하는 <strong>스냅샷(snapshot)</strong> 방식을 사용한다.</p>
<p>작업 디렉토리의 상태를 <strong><code>git add</code></strong>를 통해 <strong>Staing Area</strong>에 올리고 <strong><code>git commit</code></strong>을 하면 해당 시점의 디렉토리와 파일 구조 전체를 <strong>Tree 객체</strong>로 저장하고 <strong>이를 참조하는 Commit 객체</strong>를 생성한다. </p>
<blockquote>
<p><strong>Blob</strong>: <strong>파일의 내용을 저장하는 객체</strong>로, 파일의 실제 데이터를 고유하게 식별할 수 있는 <strong>해시값</strong>을 가진다. 파일 내용만 저장되고 파일의 이름이나 경로는 포함하지 않는다.</p>
</blockquote>
<p><strong>Tree</strong>: <strong>파일 시스템의 폴더 구조를 표현하는 객체</strong>로, <strong>하위 Blob 객체(파일 내용)</strong>과 <strong>다른 Tree 객체(서브 폴더)를 참조</strong>한다. 폴더와 파일 간의 관계를 정의한다.</p>
<br>

<h2 id="commit은-어떻게-구성될까">Commit은 어떻게 구성될까?</h2>
<p>Git은 커밋을 생성할 때, <strong>Tree 객체, 메타데이터, 부모 커밋의 해시값</strong>을 포함한 전체 커밋 정보를 기반으로 <strong>SHA-1 해시</strong>를 계산한다. 
동일한 내용으로 커밋을 한다면, 동일한 해시값이 생성되어 <strong>중복 커밋을 자동으로 방지</strong>할 수 있다.</p>
<p>Commit 객체는 <strong>부모 커밋의 해시값을 참조</strong>한다. 즉, 각 커밋은 바로 전 커밋이 무엇이었는지를 알고 있다. 
이러한 연결 덕분에 Git은 과거부터 현재까지의 <strong>이력을 선형적으로 재구성</strong>할 수 있다. </p>
<blockquote>
<p><strong>SHA-1 해시</strong>는 데이터를 고유한 고정 길이 값으로 변환하는 함수로, Git에서 각 객체(커밋, 트리, 블롭 등)를 고유하게 식별하는 데 사용된다.</p>
</blockquote>
<br>

<h2 id="commit-전-staging-area를-거친다">Commit 전, Staging Area를 거친다</h2>
<p>이러한 커밋이 생성되기 전에 <strong>Staging Area(Index)</strong>라는 중간 영역을 거친다.</p>
<p><strong>Staging Area</strong>는 변경된 파일 중 <strong>어떤 파일을 커밋에 포함시킬지 선택적으로 준비하는 공간</strong>이다. 
<strong><code>git add</code></strong>를 실행하면, Git은 해당 파일의 현재 상태에 대한 <strong>Blob 객체(파일 내용의 스냅샷)</strong>를 생성하여 Staging Area에 임시 저장한다.</p>
<p>이 단계에서는 <strong>Tree 객체나 Commit 객체는 생성되지 않으며</strong>, Git은 준비된 Blob들을 내부적으로 기록하고, 기존 상태 반영할 준비만 한다.</p>
<p>이후 <strong><code>git commit</code></strong> 명령어를 실행하면, <strong>Staging Area에 있는 Blob들을 기반으로 Tree 객체가 구성</strong>되고, <strong>이 Tree를 참조하는 Commit 객체가 생성</strong>되며,  최종적으로 <strong>Git 디렉토리(.git)</strong>에 저장된다.</p>
<br>

<h2 id="브랜치는-commit을-가리키는-포인터이다">브랜치는 Commit을 가리키는 포인터이다.</h2>
<p>Git에서 <strong>브랜치(branch)</strong>는 단순히 <strong>하나의 커밋을 가리키는 포인터</strong>에 불과하다.
우리가 흔히 알고 있는 <strong><code>main</code></strong> 브랜치나 <strong><code>feature/login</code></strong> 같은 이름들은 사실 특정 커밋의 SHA-1 해시를 가리키는 <strong>라벨(label)</strong>이다.</p>
<p>브랜치를 새로 만들면 기존 커밋에서 새로운 포인터가 생성된다. 이후 새로운 커밋이 추가되면, <strong>해당 브랜치 포인터가 최신 커밋을 따라 자동으로 이동</strong>하게 된다.</p>
<p>이 덕분에 Git은 서로 다른 작업 흐름을 브랜치 단위로 분리해 진행할 수 있으며, <strong>병합(merge)</strong>이나 <strong>리베이스(rebase)</strong> 등을 통해 <strong>브랜치 간 이력을 통합</strong>할 수 있다.</p>
<blockquote>
<p>브랜치를 옮긴다는 건, <strong>HEAD 포인터가 가리키는 브랜치를 바꾸는 것</strong>이다. 
<strong>HEAD -&gt; 브랜치 -&gt; 커밋</strong>의 연결 구조를 통해, 작업할 브랜치를 손쉽게 전환하고 관리할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 익명 클래스: 사용법, 장점, 한계]]></title>
            <link>https://velog.io/@woomin-wang/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%97%90-%EC%84%A0%EC%96%B8%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@woomin-wang/JAVA-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%97%90-%EC%84%A0%EC%96%B8%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Wed, 30 Apr 2025 07:08:05 GMT</pubDate>
            <description><![CDATA[<h2 id="익명-클래스란annoymous-class란">익명 클래스란(Annoymous Class)란?</h2>
<p><strong>익명 클래스</strong>는 일회성으로 클래스를 정의하고 바로 객체를 생성할 수 있는 강력한 기능이다. 익명 클래스를 잘 활용하면 코드의 길이를 줄이고, 특정 로직을 간단히 처리할 수 있다. 하지만 이러한 익명 클래스를 사용할 때 꼭 알아야 할 점이 있다.</p>
<h3 id="우선-왜-익명클래스인가">우선 왜 &quot;익명&quot;클래스인가?</h3>
<p>익명 클래스는 <strong>이름이 없는 클래스</strong>이다. 이를 통해 다음과 같은 특징을 가진다.</p>
<ul>
<li><strong>클래스 이름 없음</strong>: 생성자 정의할 수 없으며 재사용이 불가능</li>
<li><strong>간결함</strong>: 클래스를 선언하자마다 객체를 생성할 수 있고 짧고 간결한 코드 작성이 가능</li>
</ul>
<br>

<h3 id="익명-클래스-사용-조건">익명 클래스 사용 조건</h3>
<p>익명 클래스는 <strong>반드시 상속하거나 인터페이스를 구현</strong>해야만 사용할 수 있다. 즉, <strong>기존 클래스를 확장</strong>하거나, <strong>인터페이스를 구현하는 일회용 객체를 만들 때 사용</strong>된다.</p>
<blockquote>
<p>** 💡 익명 클래스는 기존에 정의한 클래스나 인터페이스를 재정의하여 한 번만 사용할 객체를 만드는 기법이다.**</p>
</blockquote>
<br>

<h2 id="익명-클래스-사용-예시">익명 클래스 사용 예시</h2>
<h3 id="추상-클래스-상속-예시">추상 클래스 상속 예시</h3>
<pre><code class="language-java">abstract class Animal {
    abstract void sound();
}

Animal dog = new Animal() {
    @Override
    void sound() {
        System.out.println(&quot;멍멍&quot;);
    }
};</code></pre>
<h3 id="인터페이스-구현">인터페이스 구현</h3>
<pre><code class="language-java">interface ClickListener {
    void onClick();
}

ClickListener listener = new ClickListener() {
    @Override
    public void onClick() {
        System.out.println(&quot;클릭됨&quot;);
    }
};</code></pre>
<br>

<h2 id="익명-클래스가-유용한-이유">익명 클래스가 유용한 이유</h2>
<h3 id="1-일회성-오버라이딩-용도">1. 일회성 오버라이딩 용도</h3>
<ul>
<li>익명 클래스는 <strong>한 번만 사용할 메서드</strong>를 오버라이딩하여 간편하게 처리 가능</li>
<li>코드가 간결해지고, 유지보수가 용이하다. </li>
<li>이벤트 처리나 콜백 함수에 익명 클래스를 사용하면, 복잡한 클래스를 정의하지 않고도 간단히 구현 가능</li>
</ul>
<blockquote>
<p>💡 <strong>익명 클래스</strong>는 새로운 클래스를 정의하는 것이 아니라, 이미 정의된 클래스의 멤버를 <strong>일회성으로 재정의하여 사용</strong>하는 기법이다. 즉, <strong>부모 클래스의 자원을 한 번만 재정의해서 사용</strong>하는 용도이다.</p>
</blockquote>
<h3 id="2-상속-관계로-자식-클래스-생성-가능">2. 상속 관계로 자식 클래스 생성 가능</h3>
<ul>
<li>익명 클래스는 부모 클래스를 상속하는 <strong>새로운 자식 클래스를 생성</strong>하고, 그 자식 클래스의 객체를 <strong>부모 생성자를 통해 생성</strong>한다.</li>
<li>상속 관계를 기반으로 객체가 생성되며, <strong>부모 클래스의 자원을 재정의하여 사용</strong>할 수 있다.</li>
</ul>
<br>

<h2 id="익명-클래스의-한계">익명 클래스의 한계</h2>
<h3 id="1-다중-인터페이스-구현-불가">1. 다중 인터페이스 구현 불가</h3>
<p>익명 클래스는 <strong>하나의 인터페이스만 구현할 수 있다.</strong> 여러 인터페이스를 동시에 상속하거나 구현하려면, <strong>명시적인 클래스를 선언</strong>해야 한다.</p>
<pre><code class="language-java">interface IAnimal {
    void sound();
}

interface ICreature {
    void move();
}

// ❌ 익명 클래스에서는 두 인터페이스를 동시에 구현할 수 없다
IAnimal creature = new IAnimal, ICreature() {
    @Override
    public void sound() {
        System.out.println(&quot;동물 소리&quot;);
    }
    @Override
    public void move() {
        System.out.println(&quot;이동&quot;);
    }
};
</code></pre>
<h3 id="2-클래스와-인터페이스-동시에-상속-불가">2. 클래스와 인터페이스 동시에 상속 불가</h3>
<pre><code class="language-java">abstract class MyClass {
    abstract void print();
}

interface IAnimal {
    void sound();
}

// ❌ 익명 클래스에서는 클래스를 상속하고 인터페이스를 동시에 구현할 수 없다
MyClass myObject = new MyClass() implements IAnimal {
    @Override
    public void sound() {
        System.out.println(&quot;소리&quot;);
    }
    @Override
    void print() {
        System.out.println(&quot;프린트&quot;);
    }
};
</code></pre>
<h3 id="3-새로운-메서드-외부에서-호출-불가">3. 새로운 메서드 외부에서 호출 불가</h3>
<p>기존의 자식 클래스에서는 부모 메서드를 오버라이딩하고 새로운 메서드를 추가할 수 있지만, <strong>익명 클래스에서는 오버라이딩한 메서드만 사용 가능</strong>하고, <strong>새로 정의한 메서드는 외부에서 호출할 수 없다.</strong></p>
<pre><code class="language-java">// 부모 클래스
class Animal {
    public String bark() {
        return &quot;동물이 웁니다&quot;;
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Animal() {
            // @Override 메소드
            public String bark() {
                return &quot;개가 짖습니다&quot;;
            }

            // 새로 정의한 메소드
            public String run() {
                return &quot;달리기 ㄱㄱ싱&quot;;
            }
        };

        dog.bark();
        dog.run(); // ! Error - 외부에서 호출 불가능
    }
}</code></pre>
<p>익명 클래스는 <code>Animal</code> 클래스를 상속받은 익명 자식 클래스이므로, <code>run()</code> 메서드는 <code>Animal</code> 클래스에 정의되지 않아 외부에서 호출할 수 없다. 즉, <strong>다형성</strong>에 의해 <code>Animal</code> 타입 변수에서는 부모 클래스에서 정의된 메서드만 호출 가능하다.</p>
<br>

<h2 id="람다-표현식과-비교">람다 표현식과 비교</h2>
<p>자바 8부터 <strong>람다 표현식</strong>이 도입되면서, 익명 클래스는 더 간결하게 표현할 수 있게 되었다. 특히, 인터페이스 구현에서 람다 표현식은 익명 클래스보다 더욱 간결하고, 간단한 이벤트 처리나 콜백 함수를 작성할 때 유용하다.</p>
<p><strong>람다식 표현 예시</strong></p>
<pre><code class="language-java">Runnable r = () -&gt; System.out.println(&quot;실행 중!&quot;);</code></pre>
<h2 id="결론">결론</h2>
<p>익명 클래스는 <strong>단발성 로직</strong>을 간결하게 처리할 수 있는 유용한 도구이다. 주로 <strong>이벤트 처리</strong>나 <strong>콜백 함수</strong>에 적합하며, <strong>상속</strong>이나 <strong>인터페이스 구현</strong>을 통해 빠르게 객체를 생성할 수 있다. 하지만 <strong>새로운 메서드 정의</strong>나 <strong>다중 인터페이스 구현</strong>에는 <strong>제한</strong>이 있어 복잡한 구조에는 불편할 수 있다.</p>
<p>추후에는 <strong>람다식</strong>과 <strong>익명 클래스의 차이점</strong>과, 각각 어떤 상황에서 더 적합한지에 대해 살펴보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 추상클래스와 인터페이스: 무엇을 선택할까?]]></title>
            <link>https://velog.io/@woomin-wang/JAVA-%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@woomin-wang/JAVA-%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Tue, 29 Apr 2025 11:17:11 GMT</pubDate>
            <description><![CDATA[<p><strong>추상 클래스</strong>와 <strong>인터페이스의</strong> 개념은 학습했지만, <strong>실제 개발에서 언제 어떻게 적절히 활용해야 할지에 대한 감을 잡기 어렵다.</strong> 이에 따라 상황에 맞는 사용 목적과 활용 방식을 정리하여, 객체 지향 설계에 대한 이해를 더욱 깊이 다지고자 한다.</p>
<br>

<h2 id="추상-클래스abstract-class">추상 클래스(Abstract Class)</h2>
<ul>
<li><strong>공통적인 속성(변수)과 동작(메서드)을 가진 미완성 클래스</strong>다.</li>
<li>일부 메서드는 <strong>직접 구현</strong>하고, 일부 메서드는 <strong>자식 클래스가 반드시 구현하도록 강제</strong>할 수 있다.</li>
<li><strong>인스턴스를 직접 생성할 수 없고, 반드시 상속받아서 사용</strong>해야 한다.</li>
<li><code>extends</code> 키워드로 상속하며, <strong>단일 상속만 가능</strong>하다.</li>
</ul>
<br>

<h2 id="인터페이스interface">인터페이스(Interface)</h2>
<ul>
<li>클래스가 <strong>반드시 구현해야 하는 동작 목록만 정의하는 일종의 &quot;규약&quot;</strong>이다.</li>
<li>모든 메서드는 기본적으로 <strong>구현 없이 선언</strong>만 한다. (단, Java8부터 <code>defaulut</code>, <code>static</code> 메서드 정의 가능)</li>
<li>필드는 무조건 <code>public static final</code> <strong>상수</strong>만 허용된다.</li>
<li><code>implements</code> 키워드로 구현하며, <strong>여러 개의 인터페이스를 동시에 구현할 수 있다.</strong></li>
</ul>
<br>  

<h3 id="java8-이후-인터페이스도-기본-제공-기능을-가질-수-있다">Java8 이후, 인터페이스도 &quot;기본 제공 기능&quot;을 가질 수 있다?</h3>
<p>기존에는 인터페이스가 메서드 <strong>시그니처만 선언</strong>하고, 구현은 자식 클래스가 직접 해야 했다. 하지만, Java 8부터는 인터페이스 안에서도 <code>default</code> 메서드를 통해 <strong>인터페이스도 기본 구현</strong>을 가질 수 있게 되었다.
<br></p>
<p>✔️ default 메서드 예시 코드</p>
<pre><code class="language-java">interface Animal {
    void sound();

    default void sleep() {
        System.out.println(&quot;잠을 잡니다.&quot;);
    }
}

class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println(&quot;멍멍!&quot;);
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.sound();  // 멍멍!
        dog.sleep();  // 잠을 잡니다.
    }
}
</code></pre>
<br>

<h2 id="추상-클래스-vs-인터페이스-언제-사용할까">추상 클래스 vs 인터페이스: 언제 사용할까?</h2>
<h3 id="추상-클래스-사용이-적합한-경우"><strong>추상 클래스 사용이 적합한 경우</strong></h3>
<ul>
<li><p><strong>상속 관계가 명확한 경우</strong>: 부모 클래스에서 기본 동작을 정의하고, 자식 클래스에서 이를 확장하거나 변경할 때 사용</p>
</li>
<li><p><strong>상태 공유가 필요한 경우</strong>: 자식 클래스들이 공통된 상태를 관리하거나 공유해야 할 때 유용</p>
</li>
<li><p><strong>공통 기능이 많을 때</strong>: 여러 클래스가 공통적으로 가지는 기능을 한 곳에 정의하고 이를 재사용할 때 사용</p>
</li>
</ul>
<h3 id="인터페이스-사용이-적합한-경우"><strong>인터페이스 사용이 적합한 경우</strong></h3>
<ul>
<li><p><strong>서로 관련 없는 클래스들이 같은 동작을 해야 할 때</strong>: 여러 클래스들이 공통된 기능을 구현해야 하며, 서로 다른 상속 계층을 가질 때 유용</p>
</li>
<li><p><strong>다중 상속이 필요한 경우:</strong> 자바는 다중 상속을 지원하지 않지만, 인터페이스는 여러 개를 동시에 구현할 수 있기 때문에 다양한 기능을 한 클래스에서 구현</p>
</li>
<li><p><strong>동작의 일관성을 보장하고 싶을 때</strong>: 다양한 클래스에서 동일한 동작을 강제하고 싶을 때 인터페이스를 사용</p>
</li>
</ul>
<blockquote>
<p><strong>추상 클래스</strong>: <strong>&quot;is-a&quot;</strong> 관계를 표현한다. → 객체의 <strong>본질적 속성</strong>을 설계할 때 사용.
(ex. &quot;Dog는 Animal이다.&quot;)</p>
</blockquote>
<p><strong>인터페이스</strong>: <strong>&quot;can-do&quot;</strong> 능력을 표현한다. → 객체의 <strong>부가적 기능</strong>을 설계할 때 사용.
(ex. &quot;Dog는 Bark할 수 있다.&quot;)</p>
<br>

<h2 id="추상-클래스와-인터페이스의-적절한-사용-예시">추상 클래스와 인터페이스의 적절한 사용 예시</h2>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/c42a58b2-4783-4f84-a94b-8bfce78a21d3/image.png" alt=""></p>
<p><strong>1. Creature 클래스 (추상 클래스)</strong></p>
<ul>
<li><strong>Creature</strong>는 모든 생물이 가져야 할 공통 속성과 동작을 정의한다.</li>
<li>&quot;Animal은 Creature이다&quot;, &quot;Plant는 Creature이다&quot;처럼, <strong>&quot;is-a&quot;</strong> 관계를 표현한다.</li>
</ul>
<p><strong>2. Animal, Plant 클래스 (상속)</strong></p>
<ul>
<li><strong>Animal</strong>과 <strong>Plant</strong>는 <strong>Creature</strong>를 상속받고, 각각의 고유한 동작을 구현한다.</li>
<li>상속을 통해 본질적 속성을 설계한다.</li>
</ul>
<p><strong>3. Barkable 인터페이스</strong></p>
<ul>
<li><strong>Dog</strong>와 <strong>Cat</strong>은 <strong>Animal</strong>을 상속받고, 추가로 <strong>Barkable</strong> 인터페이스를 구현한다.</li>
<li>&quot;Dog는 Bark할 수 있다&quot;, &quot;Cat은 Bark할 수 있다&quot;처럼, <strong>&quot;can-do&quot;</strong> 능력을 표현한다.</li>
<li><strong>Plant</strong>는 짖을 수 없기 때문에 <strong>Barkable</strong>을 구현하지 않는다.</li>
</ul>
<p><strong>4. Eatable 인터페이스</strong></p>
<ul>
<li>먹을 수 있는 생물들만 <strong>Eatable</strong> 인터페이스를 구현하여 <code>eat()</code> 기능을 가진다.</li>
<li>예를 들어, <strong>Dog</strong>는 먹을 수 있으므로 <code>eat()</code>을 구현하지만, <strong>Rose</strong>는 먹을 수 없기 때문에 <strong>Eatable</strong>을 구현하지 않습니다.</li>
<li>&quot;Dog는 먹을 수 있다&quot;처럼, <strong>&quot;can-do&quot;</strong> 능력을 표현한다.</li>
</ul>
<br>

<p><strong>참고 자료</strong> - <a href="https://velog.io/@new_wisdom/Java-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%B0%A8%EC%9D%B4">추상 클래스와-인터페이스의 차이</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고록] ep0. 휴학 옳은 선택일까?]]></title>
            <link>https://velog.io/@woomin-wang/%ED%9A%8C%EA%B3%A0%EB%A1%9D-ep0.-%ED%9C%B4%ED%95%99-%EC%98%B3%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@woomin-wang/%ED%9A%8C%EA%B3%A0%EB%A1%9D-ep0.-%ED%9C%B4%ED%95%99-%EC%98%B3%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Thu, 24 Apr 2025 15:35:25 GMT</pubDate>
            <description><![CDATA[<h3 id="첫-대학생활을-즐겨서-후회하냐고요">첫 대학생활을 즐겨서 후회하냐고요?</h3>
<p>코로나, 군대로 남들보다 조금 늦게 시작한 나의 대학생활은 3학년이 되어서야 비로소 ‘시작’이라는 말을 붙일 수 있었어요.</p>
<p>그렇게 허겁지겁 따라가다 보니 어느덧 4학년이 코앞이더라고요. 
준비도 마음의 여유도 없이, 시간은 늘 그렇듯 기다려주지 않았어요.</p>
<p>당시엔 학점만 잘 챙기면 된다고 생각했어요.
더 능동적일 수 있었는데, 그러지 못한 스스로가 아쉽긴 하지만… 
그때로 다시 돌아가도 같은 길을 걸었을 것 같아요.</p>
<br>

<h3 id="휴학을-한다고">휴학을 한다고?</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/e8b08ec5-6ace-4c3a-9678-75715c8d6219/image.png" alt=""></p>
<p>제 인생에서 군휴학을 제외한 일반 휴학은 애초에 선택지에 없었어요.
늘 정해진 길만 따라가야 한다고 생각했거든요.
하지만 살다 보면 알게 되죠. </p>
<p>인생은 꼭 선택지대로만 흘러가진 않는다는 걸.
결국, 내 길은 내가 개척해야 하니까요.</p>
<p>남은 시간은 1년뿐이었고, 그래서 더 조급했어요.
단 1년 안에 무언가를 이뤄내야 한다는 압박감 속에서, 제 자신이 너무 초라하게 느껴졌어요.</p>
<p>휴학과 함께 제게 찾아온 건 바로 <strong>WiSoft 랩실</strong>이었어요.
이 선택이 제 삶에 새로운 방향을 열어줄지는 그땐 몰랐어요.</p>
<br>

<h3 id="javascript-나는-백엔드가-하고-싶어">JavaScript? 나는 백엔드가 하고 싶어!</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/e8da037e-156b-4faf-b862-dd550965c6e6/image.png" alt=""></p>
<p>랩실에 들어오고 나서, 학부생들끼리 진행하는 JavaScript 세미나에 참여하게 됐어요.</p>
<p>지금 생각하면 웃긴 이야기지만, 그땐 프론트엔드와 백엔드가 정확히 뭘 하는지도 잘 몰랐어요.
그럼에도 이상하리만치 백엔드에 대한 열망은 강했죠.
아마도 성격상 하나를 깊게 파고드는 걸 좋아해서였을지도 몰라요.
<strong>그냥, 끌렸어요.</strong></p>
<p>그래서 처음엔 JavaScript를 보며
“이거 프론트 쪽 언어 아닌가?”
하는 의문부터 들었어요.
정말, 아무것도 몰랐으니까요.</p>
<p>당연히 세미나 초반엔 열정도 크지 않았어요.
그런데 재영이 형과 이런저런 이야기를 나누다 보니
JavaScript의 중요성과 필요성을 하나씩 깨닫기 시작했죠.</p>
<p>그리고 그때부터였어요.
모든 걸 빨아들이듯 공부하기 시작한 건.</p>
<p>마음가짐이 바뀌니까, 보는 것도 느끼는 것도 완전히 달라졌어요.
전공 수업처럼 수동적으로 배우는 게 아니라
하나하나 파헤치고 이해해가는 과정 자체가 너무 재밌었어요.</p>
<p>그렇게 저는 공부하는 법을 처음으로 제대로 배웠고,
이 JavaScript 세미나는 제게 정말 소중한 첫걸음이 되었습니다.</p>
<br>

<h3 id="열심히-살았다고-할-수-있는-순간이-생기다">열심히 살았다고 할 수 있는 순간이 생기다</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/73c7f7c4-4d76-454a-b6bd-44592b01077f/image.png" alt=""></p>
<p>사실 JS 세미나는 제가 병행하던 활동 중 하나였고, 주된 활동은 박사님 세미나였어요.
총 7주간의 대장정이었는데, 적응하는 데만 거의 4주가 걸렸던 것 같아요.</p>
<p>프로그래밍 언어도 낯선 상황에서 쏟아지는 지식들을 받아들이기엔 벅찼고,
처음엔 코드를 따라 치는 것만으로도 벅찼죠.
정말… 너무 어려웠어요.
뭘 말하는 건지 하나도 이해가 안 됐거든요?</p>
<p>근데 어느 날, 박사님이 내주신 과제를 하다가
‘아, 이거구나!’ 하는 순간이 찾아왔어요.
그다음 주엔 전주에 이해하지 못했던 내용들이 하나둘씩 연결되면서
틀이 잡히는 느낌이 들었고, 그게 너무 짜릿했어요.
그 느낌이 저를 더 불태우고, 계속 앞으로 나아가게 만들었어요.</p>
<p>그 이후로는 정말 매일같이 랩실에 앉아서
‘지금 내가 할 수 있는 건 뭐든 다 해보자’는 마음으로 공부했어요.</p>
<p>에러를 해결하는 것도 너무 재밌었어요.
처음엔 오류 복사해서 ChatGPT에 붙여넣는 게 전부였는데,
어느 순간부터는 에러 메시지를 스스로 해석하고,
코드를 분석해서 원인을 찾아내는 과정이 정말 즐거워졌어요.</p>
<p>작은 톱니바퀴들이 서로 맞물려
하나의 움직임을 만들어낼 때 느껴지는 그 짜릿함…
진짜, 최고예요.</p>
<p>하루 12시간씩 랩실에 앉아서
‘왜 안 되지?’를 고민했던 시간들.
돌이켜보면, 그게 제 인생에서 가장 열정적인 시기였던 것 같아요.</p>
<br>

<h3 id="나의-무지함을-깨닫는-것">나의 무지함을 깨닫는 것</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/b6f77cb7-1362-4ad3-b454-b2e82d55c40f/image.png" alt=""></p>
<p>그렇게 7주간의 세미나를 무사히 마쳤고, 마지막에 민서 누나와 재영이 형이 개발자라는 직군이 어떤 일을 하는 사람들인지, 전체적인 구조와 흐름을 설명해주셨어요.</p>
<p>설명을 듣는데, 놀랍게도 제가 알고 있는 단어들이 하나둘씩 들려오는 거예요.
그 순간이 참 신기했어요.
&#39;아, 나도 이제 뭔가 조금은 알고 있구나&#39; 하는 감정이 스치기도 했고요.</p>
<p>하지만 동시에,
‘아… 나는 정말 아무것도 몰랐구나’라는 걸 더 깊이 깨달았어요.</p>
<p>저는 제 무지함을 깨달을 때마다 참 분해요.
그리고 그 분함을 해소하기 위해 파고드는 과정이… 이상하게 재밌어요.</p>
<p>그렇게 저는 또 한 번 동기부여를 얻었고,
2025년의 1월과 2월은…
그저 속절없이, 하지만 아주 뜨겁게 흘러가버렸어요.</p>
<br>


<h3 id="자바에게-푹-빠져버렸어요">자바에게 푹 빠져버렸어요</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/f3d67658-ccdd-49d7-ba2c-5df80c548572/image.png" alt=""></p>
<p>자바 공부를 시작한 날이 2월 15일쯤이었는데, 이 글을 쓰는 시점인 4월 25일 시간이 꽤 흘렀네요.</p>
<p>JS 세미나가 끝날 무렵, 저는 독학으로 Java 공부를 시작했어요.
Java를 한 번도 배운 적이 없었지만, 박사님 세미나, 김영한님의 강의, 그리고 JS 세미나 덕분에
습득력이 꽤 빨랐어요. 
물론, 공부법도 큰 역할을 했다고 생각해요.</p>
<p>개인적으로는 학습 속도가 빠르다고 생각하지만,
그렇다고 조급하게 생각하지 않으려고 해요.
조급해지면 그만큼 놓치는 것들이 많다는 걸 느꼈거든요.</p>
<p>지금, 제가 하고 싶은 공부를 한다는 게 참으로 행복한 시기인 것 같아요.</p>
<br>

<h3 id="바뀌지-않았다면-그것도-나의-선택이다">&quot;바뀌지 않았다면, 그것도 나의 선택이다&quot;</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/486d3214-ddc5-494d-8cb4-b72fd0a025b1/image.png" alt=""></p>
<p>휴학이 옳은 선택이었다고 믿고 싶어요.
*<em>&#39;바뀌지 않았다면, 그것도 나의 선택이다.&#39;
*</em>이 문장은 제가 참 좋아하는 말이에요.
어떤 일이 일어나도, 그게 내가 한 선택이라면 그게 나의 길이라는 거죠.</p>
<p>휴학이 내린 결정이 옳았음을 증명하고 싶어요.
길다면 길고, 짧다면 짧은 반학기.
그 시간을 어떻게 보내느냐가 제 선택을 확고히 만들어줄 거예요.
저는 이 시간을 정말 알차게 보내고 싶습니다.</p>
<p><strong>&quot;지금의 내가 존재하는 이유는 내가 내려온 결정들 덕분입니다. 지금의 내 모습은 내가 결정하고 선택한 것들과 내가 만난 사람들, 내가 소비해 왔던 모든 것들의 결과물인 것입니다.&quot;</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] JVM Runtime Data Area]]></title>
            <link>https://velog.io/@woomin-wang/JAVA-JVM-Runtime-Data-Area</link>
            <guid>https://velog.io/@woomin-wang/JAVA-JVM-Runtime-Data-Area</guid>
            <pubDate>Tue, 01 Apr 2025 04:54:49 GMT</pubDate>
            <description><![CDATA[<h1 id="jvm과-runtime-data-area">JVM과 Runtime Data Area</h1>
<h3 id="jvmjava-virtual-machine이란">JVM(Java Virtual Machine)이란?</h3>
<p>JVM은 자바 애플리케이션을 실행하는 가상 머신으로, 자바 코드를 바이트코드로 변환한 후 이를 실행하는 역할을 한다. </p>
<p>이 과정에서 JVM은 <strong>메모리를 효율적으로 관리</strong>하기 위해 <strong>Runtime Data Area</strong>라는 공간을 사용한다.</p>
<h3 id="runtime-data-area란">Runtime Data Area란?</h3>
<p>Runtime Data Area는 <strong>JVM이 자바 프로그램 실행 중 사용하는 메모리 영역</strong>이다.</p>
<p>JVM의 Runtime Data Area는 다음과 같이 구성된다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/25e77afe-ca18-4180-b317-0cf16eeed17c/image.png" alt=""></p>
<h2 id="스택stack-메모리">스택(Stack) 메모리</h2>
<blockquote>
<p><strong>스택(Stack) 메모리</strong></p>
</blockquote>
<ul>
<li>메서드 호출 시 <strong>개별적인 스택 프레임</strong>이 생성되며, 실행이 종료되면 해당 <strong>스택 프레임</strong>이 자동으로 제거</li>
<li><strong>기본 타입의 값</strong>과 <strong>객체 참조 주소</strong>, <strong>메서드의 지역 변수</strong>, <strong>매개변수</strong> 등을 저장</li>
</ul>
<h3 id="스택-프레임-구조">&lt; 스택 프레임 구조&gt;</h3>
<p><strong>스택 프레임(Stack Frame)</strong>은 각 메서드 호출 시 생성되는 독립적인 메모리 블록이다. 메서드 실행 시 자신만의 스택 프레임을 생성하여, 실행이 끝나면 해당 프레임이 제거된다.</p>
<p>각 스택 프레임은 다음과 같은 영역으로 구성된다.</p>
<p><strong>1. Local Variables</strong> 
: 메서드 내에서 선언된 <strong>지역 변수</strong>와 <strong>매개변수</strong>가 저장되는 영역 </p>
<ul>
<li><strong>기본 타입 변수</strong>: 값 자체가 저장</li>
<li><strong>참조 타입 변수</strong>: 객체의 메모리 주소를 저장</li>
<li><strong>매개변수</strong>: 메서드 호출 시 전달되는 값 또는 참조를 저장</li>
</ul>
<p><strong>2. Operand Stack</strong> 
: 메서드 실행 중 발생하는 <strong>연산을 위한 스택 구조</strong>로, 메서드가 수행하는 연산을 지원하는 임시 저장 공간</p>
<ul>
<li><strong>피연산자</strong>: 연산이 필요한 값들 저장</li>
<li><strong>연산 결과</strong>: 메서드 내 연산의 결과 저장</li>
<li><strong>Push/Pop 연산</strong>:연산을 위해 값을 Push/Pop</li>
</ul>
<p><strong>3. Frame Data</strong>
: 메서드 실행에 필요한 <strong>상태 정보</strong>와 <strong>예외 처리 정보</strong>를 저장하는 영역</p>
<ul>
<li><strong>복귀 주소</strong>: 메서드 종류 후 돌아갈 주소 저장</li>
<li><strong>예외 처리 정보</strong>: 예외 발생 시 필요한 정보 저장</li>
</ul>
<h3 id="스택-메모리-동작-방식">&lt; 스택 메모리 동작 방식 &gt;</h3>
<ol>
<li><p><strong>메서드 호출</strong>: 새로운 메서드가 호출되면 <strong>스택 프레임</strong>이 생성되어 스택에 푸시</p>
</li>
<li><p><strong>Stack Frame 내부 처리</strong>:</p>
</li>
</ol>
<ul>
<li>메서드의 <strong>지역변수, 매개변수</strong>가 저장</li>
<li>연산을 수행하기 위한 <strong>Operand Stack</strong>이 생성</li>
<li>메서드 실행에 필요한 <strong>상태 정보</strong>가 포함</li>
</ul>
<ol start="3">
<li><p><strong>스택 프레임 관리</strong>: 스택 프레임은 LIFO 방식으로 Push/Pop, 메서드 실행이 끝나면 해당 스택 프레임이 <strong>자동으로 제거</strong></p>
</li>
<li><p><strong>프로그램 종료 또는 상위 메서드로 반환</strong>: 모든 메서드가 종료되면 스택이 비워짐</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/17cca591-d1e7-459e-8f43-ccd22d5bf3a2/image.png" alt=""></p>
<h3 id="스택-오버-플로우stack-overflow">&lt; 스택 오버 플로우(Stack Overflow) &gt;</h3>
<p>스택은 <strong>고정된 크기의 메모리 영역</strong>을 사용한다. 이때 메서드 호출이 너무 깊어지거나 지나치게 많은 지역 변수를 할당하면 스택이 가득 차는 <strong>스택 오버 플로우</strong>가 발생한다.
Ex) 과도한 재귀 함수</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/4029fc4c-3039-4237-9e22-b6338ce56ec0/image.png" alt=""></p>
<h2 id="힙heap-영역">힙(Heap) 영역</h2>
<blockquote>
<p><strong>힙(Heap) 메모리</strong> </p>
</blockquote>
<ul>
<li><strong>동적</strong>으로 생성된 <strong>객체</strong>나 <strong>배열</strong>이 저장되는 메모리 영역</li>
<li>프로그램 실행 중 필요에 따라 크기가 변할 수 있음</li>
<li>JVM의 <strong>Garbage Collector</strong>을 통해 자동 관리 </li>
</ul>
<h3 id="힙-영역-동작-방식">&lt; 힙 영역 동작 방식 &gt;</h3>
<ol>
<li><p><strong>객체 생성</strong>: new 키워드 등을 사용하여 객체를 생성하면 힙 영역에 할당</p>
</li>
<li><p><strong>객체 참조</strong>: 스택에 있는 참조 변수를 통해 힙 영역의 객체를 접근</p>
</li>
<li><p><strong>Garbage Collector 관리</strong>: 사용되지 않는 객체는 자동으로 제거</p>
</li>
</ol>
<h3 id="힙-메모리-구성-요소">&lt; 힙 메모리 구성 요소 &gt;</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/c5aa23ba-d27d-40a4-aecb-02234b1d08ec/image.png" alt=""></p>
<blockquote>
<p>&lt; <strong>Young Generation(Eden + Survivor)</strong>&gt;</p>
</blockquote>
<ul>
<li><strong>Eden 영역</strong>: 새롭게 생성된 객체가 먼저 할당되는 공간</li>
<li><strong>Survivor 영역(S0, S1)</strong>: Eden 영역에서 살아남은 객체가 이동하는 공간</li>
</ul>
<p><strong>&lt; Minor GC &gt;</strong></p>
<ul>
<li><strong>Eden</strong> 영역이 꽉 차면 <strong>Minor GC</strong>가 발생</li>
<li>불필요한 객체를 정리하고, 살아남은 객체는 <strong>Survivor 영역(S0, S1)</strong>으로 이동</li>
<li>S0과 S1은 교대로 사용되며, 하나의 영역에 객체가 복사된 후 다른 영역은 비어 있는 상태로 유지</li>
<li>일정 시간이 지나도 살아남은 객체는 <strong>Old Generation</strong>으로 이동 </li>
</ul>
<blockquote>
<p>&lt; <strong>Old Generation</strong> &gt;</p>
</blockquote>
<ul>
<li><strong>Young Generation</strong>에서 여러 번 <strong>Minor GC</strong>를 거쳐 살아남은 객체가 이동하는 공간</li>
<li><strong>Old Generation</strong>에 할당된 객체는 <strong>Minor GC</strong>의 대상이 아니며, <strong>Major GC(Full GC)</strong>를 통해 관리</li>
</ul>
<p><strong>&lt; Major GC(=Full GC) &gt;</strong></p>
<ul>
<li><strong>Old Generation</strong> 내의 참조되지 않는 객체를 정리</li>
<li><strong>Minor GC</strong>는 <strong>Young Generation</strong>만 처리하는 반면, <strong>Full GC</strong>는 <strong>Young Generation + Old Generation</strong> 모두 정리</li>
<li><strong>Stop-the-world</strong> 현상 발생하여 애플리케이션 실행이 일시적으로 멈춤</li>
</ul>
<p><strong>&lt; Stop-the-World &gt; **
**Garbage Collection</strong> 중에 JVM의 모든 애플리케이션 스레드가 멈추는 현상이다.
<strong>Full GC</strong>가 발생하면 Old Generation의 객체를 정리하는 과정에서 <strong>모든 객체를 검사</strong>해야 하므로, GC 작업을 진행하는 동안 애플리케이션의 실행이 중지된다.</p>
<h2 id="메서드method-영역">메서드(Method) 영역</h2>
<blockquote>
<p><strong>메서드(Method) 영역</strong>
: 프로그램을 실행하는 데 필요한 공통 데이터를 관리하는 영역으로, 프로그램의 모든 영역에서 공유한다.</p>
</blockquote>
<ol>
<li><strong>클래스 정보</strong>: JVM이 로드한 <strong>클래스의 구조</strong>와 <strong>클래스 이름</strong>, <strong>메서드</strong>, <strong>필드(변수)</strong>, <strong>상속 관계</strong> 등의 정보가 저장된다.</li>
</ol>
<ol start="2">
<li><strong>static 영역</strong>: <strong>static 변수</strong>와 <strong>static 메서드</strong>가 저장되는 공간이다. static으로 선언된 변수와 메서드는 <strong>클래스 자체</strong>에 속하며, 클래스가 메모리에 로드될 때 한 번만 할당되고 모든 객체가 공유한다.</li>
</ol>
<ol start="3">
<li><strong>런타임 상수 풀</strong>: 프로그램이 실행되는 동안 <strong>상수 값</strong>(<strong>문자열 리터럴</strong>, <strong>숫자 상수</strong>, <strong>final로 선언된 상수 등)</strong>을 저장하는 영역이다. </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 자바는 왜 컴파일과 인터프리터를 함께 사용할까?]]></title>
            <link>https://velog.io/@woomin-wang/JAVA-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC%EC%99%80-%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@woomin-wang/JAVA-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC%EC%99%80-%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 24 Mar 2025 08:48:11 GMT</pubDate>
            <description><![CDATA[<p>프로그래밍 언어로 작성된 소스 코드는 컴퓨터가 직접 이해할 수 없기 때문에 <strong>기계어로 변환</strong>해야 한다. 이 변환 과정에서 사용되는 방식이 <strong>컴파일러(Compiler)</strong>와 <strong>인터프리터(Interpreter)</strong> 두 가지다.</p>
<p>컴파일러는 <strong>전체 코드를 한 번에 번역</strong>하는 방식이고, 인터프리터는 <strong>한 줄씩 즉시 변환하여 실행</strong>하는 방식이다. 쉽게 비유하면, 컴파일러는 문서를 <strong>번역</strong>하는 번역가, 인터프리터는 실시간으로 말을 <strong>통역</strong>하는 통역사와 비슷하다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/a1af952a-c43e-4376-86e0-3a8df9c77283/image.png" alt=""></p>
<h2 id="컴파일러compiler">컴파일러(compiler)</h2>
<ul>
<li>전체 소스 코드를 <strong>한 번에 기계어로 변환하여 실행 파일을 생성</strong></li>
<li>변환이 끝난 후 실행되므로 <strong>실행 속도가 빠름</strong></li>
<li>대표적인 언어: C, C++ 등</li>
</ul>
<h2 id="인터프리터interpreter">인터프리터(interpreter)</h2>
<ul>
<li>소스 코드를 <strong>한 줄씩 즉시 변환하여 실행</strong></li>
<li>실행 도중에 변환이 이루어져 <strong>유연하지만 실행 속도가 느림</strong></li>
<li>대표적인 언어: Python, JavaScript 등</li>
</ul>
<h2 id="자바와의-연관성">자바와의 연관성</h2>
<p>자바는 컴파일러와 인터프리터의 특징을 모두 가지는 <strong>하이브리드 방식</strong>을 사용한다.</p>
<h3 id="자바의-실행-과정">자바의 실행 과정</h3>
<ol>
<li><strong>컴파일 단계</strong></li>
</ol>
<p><strong>자바 소스 코드(.java)</strong>를 <strong>javac(자바 컴파일러)</strong>를 사용하여 <strong>바이트코드(.class)</strong>로 변환한다. 하지만 이 바이트코드는 운영체제가 직접 실행할 수 없으며, JVM이 해석해야 한다.</p>
<ol start="2">
<li><strong>인터프리터 단계 + JIT 컴파일</strong>
JVM은 변환된 바이트코드를 <strong>한 줄씩 읽으며 실행</strong>한다.
즉, 이 단계에서 JVM이 인터프리터 방식으로 코드를 실행한다.</li>
</ol>
<p>하지만 바이트코드를 한 줄씩 해석하면 속도가 느려지므로, JVM은 <strong>JIT(Just-In-Time) 컴파일러</strong>를 함께 사용해 실행 속도를 최적화한다. JIT 컴파일러는 반복 실행되는 코드를 기계어로 변환하여 캐싱하고, 이후에 컴파일된 기계어를 직접 실행한다.</p>
<h2 id="jvm이-인터프리터-방식을-사용하는-이유">JVM이 인터프리터 방식을 사용하는 이유</h2>
<h3 id="1-운영체제의-독립성-유지"><strong>1. 운영체제의 독립성 유지</strong></h3>
<p><strong>C언어의 경우</strong>, 소스 코드를 gcc로 컴파일하여 OS에 맞는 실행 파일을 생성한다. 예를 들어, 리눅스에서는 .out, 윈도우에서는 .exe 파일이 만들어진다. 즉, <strong>OS마다 별도의 빌드 작업이 필요</strong>하며, 한 번 작성한 코드라도 다른 OS에서 실행하려면 다시 컴파일해야 한다.</p>
<p>반면, <strong>Java는 Write Once, Run Anywhere</strong>를 목표로 하기 때문에, 바이트코드가 어떤 운영체제에서도 실행될 수 있어야한다. 즉, <strong>운영체제와 관계 없이 실행할 수 있도록, JVM이 바이트코드를 각 OS에 맞게 해석하여 실행</strong>한다. JVM이 <strong>실행 파일을 컴파일하여 미리 만들면</strong>, 각 OS에 맞는 별도의 실행 파일을 만들어야 하고, 그럴 경우 <strong>자바 프로그램은 OS에 종속적</strong>이게 되어, 자바의 운영체제 독립성을 유지할 수 없게 된다.</p>
<h3 id="2-실행-중-동적-로딩-및-유연한-실행-환경-제공"><strong>2. 실행 중 동적 로딩 및 유연한 실행 환경 제공</strong></h3>
<p>자바는 실행 중에 <strong>필요한 클래스만 동적으로 로딩하는 방식을 사용</strong>한다. 만약 JVM이 전체 코드를 한 번에 기계어로 변환하면, 사용되지 않는 코드까지 미리 변환해야 하므로 메모리 낭비가 발생하고, 실행 중 동적 로딩도 불가능해진다.</p>
<h3 id="3-즉각적인-실행-가능"><strong>3. 즉각적인 실행 가능</strong></h3>
<p>컴파일 방식은 <strong>전체 코드를 변환한 후 실행</strong> 하므로, 실행 파일을 생성할 때 시간이 오래 걸린다. 하지만, 인터프리터 방식은 소스 코드를 미리 모두 변환하지 않고 실행 시점에 한 줄씩 해석하기 때문에, <strong>프로그램을 빠르게 실행</strong>할 수 있다. </p>
<h3 id="4-jit-컴파일러와-함께-사용하여-성능-최적화"><strong>4. JIT 컴파일러와 함께 사용하여 성능 최적화</strong></h3>
<p>JVM은 <strong>초반에는 인터프리터 방식으로 빠르게 실행</strong>하고, <strong>반복 실행되는 코드만 JIT 컴파일러가 기계어로 변환</strong>하여 <strong>최적의 성능</strong>을 낼 수 있도록 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 자바에서 오버라이딩 메서드가 실행되는 원리]]></title>
            <link>https://velog.io/@woomin-wang/Java-%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9-%EB%A9%94%EC%84%9C%EB%93%9C%EA%B0%80-%EC%8B%A4%ED%96%89%EB%90%98%EB%8A%94-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@woomin-wang/Java-%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9-%EB%A9%94%EC%84%9C%EB%93%9C%EA%B0%80-%EC%8B%A4%ED%96%89%EB%90%98%EB%8A%94-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Fri, 21 Mar 2025 07:29:56 GMT</pubDate>
            <description><![CDATA[<h1 id="오버라이딩-시-자식-메서드가-무조건-실행되는-이유">오버라이딩 시 자식 메서드가 무조건 실행되는 이유?</h1>
<p>자바에서 오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 재정의하는 기능입니다. 그런데 오버라이딩된 메서드는 무조건 자식 클래스의 메서드가 실행되는데, 그 이유는 <strong>동적 바인딩(Dynamic Binding)</strong> 때문입니다.</p>
<h2 id="동적-바인딩dynamic-binding이란">동적 바인딩(Dynamic Binding)이란?</h2>
<p><strong>동적 바인딩이</strong>란 <strong>컴파일 시점</strong>이 아니라 <strong>실행 시점</strong>에 실제 호출된 메서드가 결정되는 것을 말합니다. 즉, 어떤 메서드가 실행될지는 프로그램이 실제로 실행되는 동안, <strong>참조 변수의 실제 인스턴스 타입</strong>에 따라 결정된다는 것입니다.</p>
<h3 id="컴파일-시점-vs-실행-시점">컴파일 시점 vs 실행 시점</h3>
<p>자바는 <strong>컴파일 시점</strong>과 <strong>실행 시점</strong>을 명확히 구분합니다. 각 시점에는 수행되는 작업은 다음과 같습니다.</p>
<ul>
<li><strong>컴파일 시점</strong>: <strong>참조 타입</strong>을 기준으로 호출할 수 있는 메서드를 결정합니다. 이 시점에서는 <strong>참조 변수의 타입</strong>에 따라서 호출할 수 있는 메서드가 <strong>제한</strong>됩니다.</li>
<li><strong>실행 시점</strong>: 실제 <strong>인스턴스 타입</strong>을 기준으로 실행할 메서드가 결정됩니다. 즉, 프로그램이 실행될 때, 참조 변수에 <strong>실제 할당된 객체(인스턴스)의 타입</strong>에 따라 메서드가 호출됩니다.</li>
</ul>
<p>즉, 컴파일 시점에서는 어떤 메서드를 호출할 수 있는지 제한되지만, 실제 메서드 실행은 인스턴스 타입에 따라 결정됩니다.</p>
<h3 id="동적-바인딩이-발생하지-않는-경우">동적 바인딩이 발생하지 않는 경우</h3>
<p>Child chile = new Child();와 같은 경우, 참조 타입과 인스턴스 타입이 동일하기 때문에 <strong>동적 바인딩</strong>이 발생하지 않습니다. 이 경우, <strong>실행 시점에 호출된 메서드는 이미 컴파일 시점에 결정</strong>됩니다.</p>
<h2 id="parent-parent1--new-child-에서-메서드-호출-과정-정리">Parent parent1 = new Child(); 에서 메서드 호출 과정 정리</h2>
<h3 id="1-컴파일-시점">1. 컴파일 시점</h3>
<ul>
<li><strong>parent1.메서드()</strong>를 호출하면, <strong>참조 타입(Parent)</strong>을 기준으로 <strong>해당 메서드가 Parent 클래스에 존재</strong>하는지 확인합니다.</li>
<li>만약, Parent에 해당 메서드가 없다면, 컴파일 오류가 발생합니다.</li>
<li>하지만 Parent 클래스에 해당 메서드가 있다면, 컴파일 문제없이 실행됩니다.</li>
</ul>
<h3 id="2-실행-시점">2. 실행 시점</h3>
<ul>
<li>프로그램 실행 시, <strong>parent1의 인스턴스 타입은 Child</strong>입니다.</li>
<li>Child 클래스에서 오버라이딩된 메서드가 존재한다면, Parent의 메서드가 아닌 <strong>Child의 메서드가 실행</strong>됩니다.</li>
<li>이 과정을 <strong>동적 바인딩</strong>이라고 합니다. 즉, 실제 실행되는 메서드는 참조 변수의 실제 <strong>인스턴스 타입</strong>에 따라 결정됩니다.</li>
</ul>
<pre><code class="language-java">class Parent {
    void show() {
        System.out.println(&quot;Parent의 show()&quot;);
    }
}

class Child extends Parent {
    @Override
    void show() {
        System.out.printn(&quot;Child의 show()&quot;);
    }
}

public class Main {
    public static void main(String[] args) {
        Parent parent1 = new Child(); //업캐스팅 (참조 타입: Parent, 인스턴스 타입: Child)
        parent1.show();/ //실행 결과: &quot;Child의 show()&quot;
    }
}</code></pre>
<ol>
<li>컴파일 시점</li>
</ol>
<ul>
<li>parent1의 <strong>참조 타입</strong>은 <strong>Parent</strong>입니다.</li>
<li>show() 메서드가 <strong>Parent</strong> 클래스에 존재하므로 컴파일 시 오류가 발생하지 않습니다.</li>
</ul>
<ol start="2">
<li>실행 시점</li>
</ol>
<ul>
<li>실제 객체(parent1)의 <strong>인스턴스 타입</strong>은 <strong>Child</strong>입니다.</li>
<li><strong>Child</strong> 클래스에서 오버라이딩된 show() 메서드가 실행됩니다.</li>
</ul>
<h2 id="결론">결론</h2>
<p>✔ <strong>컴파일 시점</strong>에서는 <strong>참조 타입</strong>에 따라 메서드를 호출할 수 있습니다.</p>
<p>✔ <strong>실행 시점</strong>에서는 <strong>실제 인스턴스 타입</strong>에 따라 오버라이딩된 메서드가 실행됩니다.</p>
<p>✔ <strong>동적 바인딩(Dynamic Binding)</strong> 덕분에, 자식 클래스의 메서드는 무조건 실행됩니다.</p>
<p>✔ <strong>Parent 클래스에만 있는 메서드는 컴파일 시점에 호출</strong>되지만, 자식 클래스에서 오버라이딩한 메서드는 실행 시점에 결정되어 실행됩니다.</p>
<p>✔ <strong>동적 바인딩이 발생하지 않는 경우</strong>, 참조 타입과 인스턴스 타입이 동일하면 실행 시점에 호출되는 메서드가 이미 컴파일 시점에 결정됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 자바 다형성- 참조 타입과 인스턴스 타입의 관계]]></title>
            <link>https://velog.io/@woomin-wang/JAVA-%EC%9E%90%EB%B0%94-%EB%8B%A4%ED%98%95%EC%84%B1-%EC%B0%B8%EC%A1%B0-%ED%83%80%EC%9E%85%EA%B3%BC-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%ED%83%80%EC%9E%85%EC%9D%98-%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@woomin-wang/JAVA-%EC%9E%90%EB%B0%94-%EB%8B%A4%ED%98%95%EC%84%B1-%EC%B0%B8%EC%A1%B0-%ED%83%80%EC%9E%85%EA%B3%BC-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%ED%83%80%EC%9E%85%EC%9D%98-%EA%B4%80%EA%B3%84</guid>
            <pubDate>Fri, 21 Mar 2025 06:10:20 GMT</pubDate>
            <description><![CDATA[<p>자바 다형성을 학습하던 중, 참조 타입, 호출자 타입, 인스턴스 타입 등의 개념이 서로 헷갈려 제대로 이해하지 못하는 경험이 있을 것입니다. 특히, 다형성이 어떻게 작동하는지 이해하기 위해서는 이러한 타입의 관계를 명확히 구분하는 것이 중요합니다. 이번 글에서는 이 세 가지 타입의 차이점과 이를 어떻게 활용할 수 있는지에 대해 자세히 살펴보겠습니다.</p>
<h1 id="참조-타입reference-type">참조 타입(Reference Type)</h1>
<p><strong>참조 타입</strong>은 변수로 선언할 때 사용한 타입을 의미합니다. 이는 <strong>참조 변수</strong>가 접근할 수 있는 <strong>필드</strong>와 <strong>메서드</strong>가 무엇인지 결정합니다. 하지만 실제로 어떤 메서드가 실행될지는 인스턴스 타입(실제 객체의 타입)에 따라 결정됩니다.</p>
<pre><code class="language-java">Parent parent1 = new Child();</code></pre>
<ul>
<li>parent1의 <strong>참조 타입</strong>: Parent</li>
<li>parent1의 <strong>인스턴스 타입</strong>: Child</li>
</ul>
<p>parent1의 <strong>참조 타입</strong>은 Parent이지만, 실제로 parent1이 참조하는 객체는 Child 클래스의 인스턴스라는 것입니다.</p>
<h1 id="호출자-타입caller-type">호출자 타입(Caller Type)</h1>
<p><strong>호출자 타입</strong>은 <strong>메서드를 호출한 변수의 타입</strong>을 의미합니다. 메서드를 <strong>어떤 참조 변수로 호출하느냐에 따라 호출자 타입이 결정</strong>되며, 이를 통해 호출할 수 있는 메서드가 결정됩니다. 즉, 참조 변수의 타입에 따라 호출할 수 있는 메서드가 제한됩니다.</p>
<pre><code class="language-java">class Parent {
    void show() {
        System.out.println(&quot;Parent의 show()&quot;);
    }
}

class Child extends Parent {
    void show() {
        System.out.println(&quot;Child의 show()&quot;);
    }
}

public class Main {
    public static void main(String[] args) {
        Parent parent1 = new Child(); // 참조 타입: Parent, 인스턴스 타입: Child
        parent1.show(); // 호출자 타입: Parent, 실행되는 메서드는 Child의 show()
    }
}</code></pre>
<p>parent1.show()를 호출할 때, <strong>호출자 타입</strong>은 Parent지만, 실제로 실행되는 메서드는 Child의 show() 메서드입니다. 이는 <strong>메서드 오버라이딩</strong> 때문에 발생한 다형성입니다. 즉, <strong>호출자 타입</strong>은 Parent이지만 실제로 실행되는 메서드는 <strong>인스턴스 타입</strong>인 Child의 메서드입니다.</p>
<h1 id="인스턴스-타입instance-type">인스턴스 타입(Instance Type)</h1>
<p><strong>인스턴스 타입</strong>은 실제로 생성된 객체의 타입을 의미합니다. new 키워드를 사용해 생성된 객체의 클래스 타입이 바로 인스턴스 타입입니다. 중요한 점은 <strong>참조 변수의 타입과 인스턴스 타입은 다를 수 있다는 것</strong>입니다. 이를 <strong>업캐스팅(Upcasting)</strong>이라고 하며, 업캐스팅을 통해 부모 클래스 타입의 변수로 자식 클래스 객체를 참조할 수 있습니다.</p>
<pre><code class="language-java">Parent parent1 = new Child();</code></pre>
<ul>
<li>new Child();로 생성된 객체의 실제 타입은 Child입니다.</li>
<li>parent1의 타입은 Parent이지만, <strong>인스턴스 타입</strong>은 Child입니다.</li>
</ul>
<br>

<h2 id="결론">결론</h2>
<blockquote>
<p>다형성은 <strong>메서드 오버라이딩</strong>을 통해 실제 객체의 타입에 맞는 메서드가 실행되도록 하는 중요한 개념입니다. 이를 이해하기 위해서는 <strong>참조 타입</strong>, <strong>호출자 타입</strong>, <strong>인스턴스 타입</strong>의 차이를 구분하는 것이 필수적입니다. <strong>참조 타입</strong>은 변수 선언 시 사용한 타입이고, <strong>호출자 타입</strong>은 메서드를 호출한 변수의 타입, <strong>인스턴스 타입</strong>은 실제 객체의 타입을 의미합니다. 이들 간의 관계를 명확히 이해하면, 다형성의 작동 원리와 메서드가 어떻게 실행되는지 더욱 잘 파악할 수 있습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] - JDK, JRE, JVM]]></title>
            <link>https://velog.io/@woomin-wang/JAVA-JVM%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@woomin-wang/JAVA-JVM%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Tue, 11 Mar 2025 07:09:25 GMT</pubDate>
            <description><![CDATA[<h2 id="jdk는-무엇일까">JDK는 무엇일까?</h2>
<hr>
<p>JDK는 자바 개발키트(Java Development Kit)의 약자로, <strong>개발자들이 자바로 개발</strong>하는데 사용되는 SDK키트이다.</p>
<h3 id="sdk란">SDK란?</h3>
<p>소프트웨어 개발 키트로, 프로그램을 만들 때 필요한 도구와 자원을 모아 놓은 것이다. 여기에는 디버거, 컴파일러, 라이브러리뿐만 아니라, 문서, 튜토리얼, API 등이 포함되어 개발자가 쉽게 애플리케이션을 만들고 실행할 수 있도록 도와준다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/2d8fc1c4-53b5-40fe-948d-61bffffc7279/image.png" alt=""></p>
<p>JDK 안에는 개발 시 필요한 라이브러리들과 javac, jar, jdb 등의 개발 도구들이 포함되어 있고, 개발을 하려면 자바 프로그램을 실행도 시켜줘야 하기 때문에 JRE(Java Runtime Enviroment)도 포함되어 있다.</p>
<h2 id="jre의-구성요소는-무엇일까">JRE의 구성요소는 무엇일까?</h2>
<hr>
<p>JRE(Java Runtime Enviroment)는 자바 애플리케이션을 실행하기 위한 환경으로, JVM, 기본 라이브러리, 구성 파일로 구성된다. <strong>JRE는 자바 프로그램을 실행하는 데 필요한 요소만 포함하며, 개발 도구(javac, jdb)는 포함되지 않는다.</strong> 즉, JDK에는 개발 도구와 JRE가 포함되며, JRE 안에는 JVM이 포함된 구조이다.</p>
<p>기본 라이브러리는 프로그램 실행에 필요한 기능을 제공하고. 구성 파일은 실행 환경을 설정하는 역할을 한다.</p>
<h2 id="jvm은-무엇이고-왜-사용할까">JVM은 무엇이고 왜 사용할까?</h2>
<hr>
<h3 id="가상머신이란"><strong>가상머신</strong>이란?</h3>
<p>가상머신은 물리적인 하드웨어와 독립적으로 프로그램을 실행할 수 있는 소프트웨어 환경을 제공한다. <strong>하드웨어에 의존하지 않으므로, 다양한 운영체제에서 동일한 프로그램을 실행할 수 있다.</strong></p>
<h3 id="jvmjava-virtual-machine은">JVM(Java Virtual Machine)은?</h3>
<p>자바 프로그램을 실행하기 위한 가상머신인 JVM은 <strong>자바 바이트 코드를 해석하고 실행</strong>하는 역할을 하며, 이를 통해 다양한 OS에서 실행될 수 있다. 이 덕분에 개발자는 소스 코드를 작성할 때 운영체제에 의존하지 않고 프로그램을 개발할 수 있다. 다만, <strong>JVM 자체는 운영체제에 종속적</strong>이며, 각 운영체제에 맞는 JVM이 필요하다.</p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/f3dcd351-2eb5-433d-9862-5c1d9807b7b4/image.png" alt=""></p>
<h3 id="1-클래스-로더class-loader">1. 클래스 로더(Class Loader)</h3>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/e98161d8-f546-4076-a106-c5b580b4c922/image.png" alt=""></p>
<p>클래스 로더는 JVM 내로 클래스 파일(.class)을 <strong>동적으로 로드</strong>하고, <strong>Runtime Data Area에 메모리를 할당</strong>하여 실행 가능한 상태로 만든다. 프로그램 실행 시 필요한 클래스만 동적으로 적재하여 메모리 사용을 최적화한다.</p>
<h3 id="2런타임-데이터-영역runtime-data-area">2.런타임 데이터 영역(Runtime Data Area)</h3>
<p>JVM이 실행하는 동안 데이터를 저장하는 메모리 공간이다.</p>
<ul>
<li><p><strong>힙(Heap)</strong>: 객체 저장</p>
</li>
<li><p><strong>스택(Stack)</strong>: 메서드 호출, 로컬 변수 저장</p>
</li>
<li><p><strong>메소드 영역(Method Area)</strong>: 클래스 관련 데이터, 정적 변수, 메서드 코드, 상수 저장</p>
</li>
</ul>
<h3 id="3-실행-엔진execution-engine">3. 실행 엔진(Execution Engine)</h3>
<p>실행 엔진은 자바 바이트코드를 실제 하드웨어에서 실행 가능한 코드로 변환한다.</p>
<ul>
<li><p><strong>인터프리터(Interpreter)</strong>: 바이트코드를 <strong>명령어를 하나씩 읽어서 해석하고 즉시 실행</strong>한다. JVM안에서 바이트코드는 기본적으로 인터프리터 방식으로 동작한다.</p>
</li>
<li><p><strong>JIT 컴파일러(Just-In-Time)</strong>: <strong>자주 실행되는 바이트코드</strong>를 <strong>네이티브 코드로 변환하여 캐싱</strong>하고, 이후에는 네이티브 코드를 직접 실행하여 성능을 향상시킨다.</p>
</li>
<li><p><strong>Garbage Collector</strong>: 메모리 관리를 자동으로 처리하여, <strong>Heap 영역에서 더 이상 사용되지 않는 객체를 정리</strong>한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] - 배열과 List, ArrayList의 차이점]]></title>
            <link>https://velog.io/@woomin-wang/Java-%EB%B0%B0%EC%97%B4%EA%B3%BC-List-ArrayList%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@woomin-wang/Java-%EB%B0%B0%EC%97%B4%EA%B3%BC-List-ArrayList%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Wed, 05 Mar 2025 07:06:02 GMT</pubDate>
            <description><![CDATA[<p>#</p>
<h2 id="배열array">배열(Array)<img src="https://velog.velcdn.com/images/woomin-wang/post/4487cc65-296d-443b-a347-44bf8598688b/image.png" alt=""></h2>
<hr>
<blockquote>
<p><strong>같은 타입의 요소를 연속적으로 저장</strong>하는 자료구조로, <strong>크기가 고정</strong>되어 있으며 빠른 인덱스 접근이 가능하다.</p>
</blockquote>
<pre><code class="language-java">int[] arr1 = new int[5]; //크기가 5인 배열 생성

int[] arr2 = new int[] {1, 2, 3}; // 초기값을 설정한 배열 생성

int[] arr3 = {1, 2, 3, 4, 5}; // 배열을 직접 초기화</code></pre>
<ul>
<li><p><strong>고정 크기</strong>: 생성 시 크기를 지정, 변경 불가.</p>
</li>
<li><p><strong>동일한 타입</strong>: 하나의 배열에는 같은 타입만 저장.</p>
</li>
<li><p><strong>삽입/삭제 비효율적</strong>: 중간에 삽입/삭제 시 성능 저하.</p>
</li>
<li><p><strong>연속된 메모리 공간</strong>: 빠른 접근이 가능.</p>
</li>
<li><p><strong>초기화</strong>: 배열은 선언 시 초기화되지 않으면 기본값으로 초기화.
(숫자배열은 0, 객체 배열은 null로 초기화)</p>
</li>
</ul>
<h2 id="list">List</h2>
<hr>
<blockquote>
<p>순차적으로 데이터를 저장하고 <strong>중복을 허용</strong>하며 자료구조를 정의하는 <strong>인터페이스</strong>이다.
ArrayList, LinkedList 등의 다양한 구현체를 가진다.</p>
</blockquote>
<pre><code class="language-java">List&lt;Integer&gt; list = new ArrayList&lt;Integer&gt;();
list = new LinkedList&lt;Integer&gt;();</code></pre>
<ul>
<li><p><strong>유연성</strong>: List 인터페이스로 선언하면, 구현체 변경 가능.</p>
</li>
<li><p><strong>다형성</strong>: ArrayList와 LinkedList등 다양한 자료구조 사용 가능.</p>
</li>
</ul>
<h3 id="arraylist">ArrayList</h3>
<blockquote>
<p>java.util.List 인터페이스를 구현한 <strong>클래스</strong>로, <strong>가변 크기 배열</strong>을 기반으로 하여 크기 조정이 가능하며, 배열보다 사용하기 편리한 자료구조이다.</p>
</blockquote>
<pre><code class="language-java">  ArrayList&lt;Integer&gt; arrayList1 = new ArrayList&lt;Integer&gt;();

  List&lt;Integer&gt; arrayList2 = new ArrayList&lt;Integer&gt;();
</code></pre>
<h3 id="arraylist의-특징">ArrayList의 특징</h3>
<ul>
<li><p><strong>클래스</strong>: ArrayList는 List 인터페이스를 구현한 구체적인 클래스이다.</p>
</li>
<li><p><strong>가변 크기</strong>: 필요에 따라 자동으로 크기가 조정된다.</p>
</li>
<li><p><strong>중복 허용</strong>: 동일한 값을 여러 번 저장 가능하다.</p>
</li>
<li><p><strong>순차적 저장</strong>: 저장된 요소의 순서가 유지된다.</p>
<table>
<thead>
<tr>
<th align="left"><center>메서드</center></th>
<th align="right"><center>설명</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><center>add(E e)</center></td>
<td align="right"><center>요소 추가</center></td>
</tr>
<tr>
<td align="left"><center>get(int index)</center></td>
<td align="right"><center>특정 인덱스의 요소 가져오기</center></td>
</tr>
<tr>
<td align="left"><center>remove(int index)</center></td>
<td align="right"><center>특정 인덱스의 요소 삭제</center></td>
</tr>
<tr>
<td align="left"><center>size()</center></td>
<td align="right"><center>리스트 크기 확인</center></td>
</tr>
<tr>
<td align="left"><center>clear()</center></td>
<td align="right"><center>모든 요소 삭제</center></td>
</tr>
<tr>
<td align="left"><center>contains(E e)</center></td>
<td align="right"><center>특정 요소 포함 여부 확인</center></td>
</tr>
</tbody></table>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[MyBatis에서 NullPointerException 해결하기: resultMap으로 매핑 문제 해결 ]]></title>
            <link>https://velog.io/@woomin-wang/MyBatis%EC%97%90%EC%84%9C-NullPointerException-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-resultMap%EC%9C%BC%EB%A1%9C-%EB%A7%A4%ED%95%91-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@woomin-wang/MyBatis%EC%97%90%EC%84%9C-NullPointerException-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-resultMap%EC%9C%BC%EB%A1%9C-%EB%A7%A4%ED%95%91-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Sun, 16 Feb 2025 13:06:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/woomin-wang/post/68023294-34a1-4b87-b16f-e4c71b6f5b2a/image.png" alt=""></p>
<h2 id="문제-발생">문제 발생</h2>
<p>MyBatis를 사용해 Department 정보를 조회하는 Departments() 메소드를 구현했지만, 조회된 데이터가 모두 null로 반환되는 문제가 발생했습니다.
<img src="https://velog.velcdn.com/images/woomin-wang/post/902e2377-0cce-446a-8735-ae2dc6202e44/image.png" alt=""></p>
<h2 id="문제-해결을-위한-노력">문제 해결을 위한 노력</h2>
<ul>
<li><p>타입 확인: 먼저 타입이 올바른지 확인했습니다. 데이터베이스에서 가져온 값들이 자바 객체에 잘 매핑될 수 있도록 필드와 타입을 점검했습니다.</p>
</li>
<li><p>@Autowired 시도: @Autowired를 사용하여 매퍼 인터페이스를 자동 주입해봤으나, 문제는 여전히 해결되지 않았습니다.</p>
</li>
<li><p>다른 DB로 테스트: 이후, 동일한 방식으로 다른 student 테이블을 조회해봤는데, 이상 없이 데이터가 정상적으로 반환되었습니다. 이를 통해 MyBatis 설정 자체에는 문제가 없음을 알게 되었습니다.</p>
</li>
<li><p>디버깅 과정: 디버깅을 통해 11개의 데이터가 조회된다는 사실을 확인하면서, 데이터베이스와의 연결은 정상적임을 알게 되었습니다. 그 이후 문제는 매핑에 있다는 것을 깨달았습니다.</p>
</li>
</ul>
<h2 id="문제의-원인-파악">문제의 원인 파악</h2>
<p>디버깅 과정에서 데이터베이스와 자바 객체 간 매핑이 실패한 원인을 파악할 수 있었습니다. 자바는 기본적으로 카멜 케이스(camelCase) 방식으로 변수명을 사용하고, 데이터베이스에서는 스네이크 케이스(snake_case) 방식으로 컬럼명이 정의됩니다. 예를 들어, 데이터베이스에서는 dept_code, dept_name과 같은 컬럼명이 사용되지만, 자바 클래스에서는 deptCode, deptName과 같은 필드명을 사용합니다.</p>
<p>이처럼 자바 객체의 필드명과 데이터베이스 컬럼명이 일치하지 않으면, MyBatis는 이를 자동으로 매핑할 수 없습니다. 그래서 데이터베이스에서 가져온 값들이 모두 null로 반환되는 문제가 발생한 것입니다.</p>
<h2 id="자바-필드명-수정">자바 필드명 수정</h2>
<p>처음에는 자바의 필드명을 데이터베이스 컬럼명에 맞게 수정하면 정상적으로 동작할 것이라는 의문이 들었습니다. 그래서 자바 변수명을 DB 컬럼명과 일치시켰더니, 예상대로 데이터가 정상적으로 반환되었습니다. 이를 통해 자바 변수명과 데이터베이스 컬럼명이 일치하지 않으면 매핑 오류가 발생한다는 점을 확인할 수 있었습니다.</p>
<p>하지만 이는 바람직한 방식은 아닙니다. 자바에서는 카멜 케이스를 사용하는 것이 표준이므로, 보다 유연하고 올바른 해결 방법이 필요했습니다.
<img src="https://velog.velcdn.com/images/woomin-wang/post/5e8a2fd7-e45e-4550-89f9-a0c8c3090de0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/woomin-wang/post/d65dc2d8-c063-47d5-b50e-814c83050d74/image.png" alt=""></p>
<h1 id="해결-방안-3가지">해결 방안 3가지</h1>
<h2 id="1-resultmap의-property-설정-적용하기">1. resultMap의 property 설정 적용하기</h2>
<p><strong>resultMap</strong>을 사용하여 매핑을 명시적으로 설정했습니다. 
resultMap을 통해 데이터베이스 컬럼과 자바 클래스 필드를 정확히 매핑할 수 있었습니다.</p>
<pre><code>&lt;mapper namespace=&quot;wisoft.entertainment.EntertainmentService&quot;&gt;

    &lt;resultMap id=&quot;departmentResultMap&quot; type=&quot;wisoft.entertainment.Department&quot;&gt;
        &lt;result property=&quot;deptCode&quot; column=&quot;dept_code&quot;/&gt;
        &lt;result property=&quot;deptName&quot; column=&quot;dept_name&quot;/&gt;
    &lt;/resultMap&gt;

    &lt;select id=&quot;getDepartments&quot; resultMap=&quot;departmentResultMap&quot;&gt;
        SELECT * FROM department
    &lt;/select&gt;

&lt;/mapper&gt;</code></pre><h2 id="2해당-sql문에-별칭as-alias-설정하기">2.해당 SQL문에 별칭(AS, Alias) 설정하기</h2>
<p>SQL문에서 컬럼의 별칭(Alias)을 설정하여 매핑을 명시적으로 지정했습니다.
SQL문에서 AS 키워드를 사용하여 데이터베이스 컬럼 이름과 자바 클래스 필드를 일치시킬 수 있었습니다.</p>
<pre><code>&lt;select id=&quot;getDepartments&quot; resultType=&quot;wisoft.entertainment.Department&quot;&gt;
    SELECT dept_code AS deptCode, dept_name AS deptName, dept_loc AS deptLoc
    FROM department
&lt;/select&gt;
</code></pre><h2 id="3-mybatis-configuration-파일xml-설정하기">3. MyBatis configuration 파일(xml) 설정하기</h2>
<p>MyBatis 설정 파일에서 mapUnderscoreToCamelCase 옵션을 활성화했습니다. 이 설정을 적용하면 자동으로 데이터베이스의 언더스코어(_) 표기법을 자바의 카멜케이스 표기법으로 변환할 수 있습니다.</p>
<pre><code>&lt;configuration&gt;

    &lt;settings&gt;
        &lt;setting name=&quot;mapUnderscoreToCamelCase&quot; value=&quot;true&quot;/&gt;
    &lt;/settings&gt;

&lt;/configuration&gt;
</code></pre><h2 id="결론">결론</h2>
<ul>
<li><p>자동 매핑 미사용 시 resultMap 사용: 데이터베이스 컬럼명과 자바 필드명이 다를 때, resultMap을 통해 명시적으로 매핑을 지정하는 것이 중요</p>
</li>
<li><p>매핑 불일치를 해결: resultMap을 사용하면 데이터베이스와 자바 객체 간의 매핑 불일치를 해결할 수 있습니다.</p>
</li>
</ul>
<p>이번 문제를 해결하면서 MyBatis에서 매핑 문제를 처리하는 방법에 대해 더 깊이 이해하게 되었습니다.</p>
]]></description>
        </item>
    </channel>
</rss>