<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>u_soi.log</title>
        <link>https://velog.io/</link>
        <description>백엔드 개발자의 기술과 경험을 공유하는 개발 블로그입니다.</description>
        <lastBuildDate>Thu, 19 Sep 2024 15:21:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>u_soi.log</title>
            <url>https://velog.velcdn.com/images/u_soi/profile/2549d60f-f526-4469-a4e0-f7b3332d8780/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. u_soi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/u_soi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[인프콘 2024 토비의 클린 스프링]]></title>
            <link>https://velog.io/@u_soi/%EC%9D%B8%ED%94%84%EC%BD%98-2024-%ED%86%A0%EB%B9%84%EC%9D%98-%ED%81%B4%EB%A6%B0-%EC%8A%A4%ED%94%84%EB%A7%81</link>
            <guid>https://velog.io/@u_soi/%EC%9D%B8%ED%94%84%EC%BD%98-2024-%ED%86%A0%EB%B9%84%EC%9D%98-%ED%81%B4%EB%A6%B0-%EC%8A%A4%ED%94%84%EB%A7%81</guid>
            <pubDate>Thu, 19 Sep 2024 15:21:03 GMT</pubDate>
            <description><![CDATA[<p><a href="https://tobyepril.tistory.com/10">https://tobyepril.tistory.com/10</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토비의 스프링 1장 마무리]]></title>
            <link>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1%EC%9E%A5-%EB%A7%88%EB%AC%B4%EB%A6%AC</link>
            <guid>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1%EC%9E%A5-%EB%A7%88%EB%AC%B4%EB%A6%AC</guid>
            <pubDate>Thu, 19 Sep 2024 15:17:35 GMT</pubDate>
            <description><![CDATA[<h3 id="1-싱글톤-레지스트리-singleton-registry">1. 싱글톤 레지스트리 (Singleton Registry)</h3>
<p>싱글톤은 객체를 한 번만 생성하고 여러 곳에서 이를 재사용할 수 있도록 하는 디자인 패턴입니다. 싱글톤 레지스트리는 이러한 싱글톤 객체를 관리하는 컨테이너 역할을 합니다.
스프링은 기본적으로 모든 빈(bean)을 싱글톤으로 관리합니다. 빈을 한 번 생성해 컨테이너에서 관리하고, 필요한 곳에서 꺼내 사용할 수 있도록 합니다.</p>
<h3 id="2-ioc-inversion-of-control">2. IoC (Inversion of Control)</h3>
<p>제어의 역전(IoC)은 객체 생성 및 의존성 관리를 개발자가 아닌 컨테이너(스프링)에 맡기는 것을 의미합니다.
IoC를 통해 객체 간의 결합도를 낮출 수 있으며, 유지보수 및 테스트가 쉬워집니다.
스프링은 IoC 컨테이너로 작동하여 애플리케이션의 객체 생성과 소멸을 관리합니다.</p>
<h3 id="3-di-dependency-injection">3. DI (Dependency Injection)</h3>
<p>의존성 주입(DI)은 객체의 의존성을 외부에서 주입하는 것을 의미합니다.
생성자 주입, 세터 주입, 필드 주입과 같이 다양한 방식으로 DI를 구현할 수 있습니다.
DI를 통해 클래스 내부에서 객체를 생성하는 대신, 외부에서 객체를 주입받음으로써 유연하고 확장 가능한 코드를 작성할 수 있습니다.</p>
<p>아래 코드는 스프링 프레임워크에서 싱글톤 레지스트리, IoC, DI의 개념을 설명하는 간단한 예시입니다.</p>
<pre><code class="language-java">// UserRepository.java
import org.springframework.stereotype.Repository;

@Repository // 싱글톤 빈으로 등록
public class UserRepository {
    public String findUser() {
        return &quot;User1&quot;;
    }
}

// UserService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service // 싱글톤 빈으로 등록
public class UserService {
    private final UserRepository userRepository;

    // 생성자 주입 (DI)
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public String getUser() {
        return userRepository.findUser();
    }
}

// Application.java (스프링 애플리케이션 실행)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);

        // 스프링 컨테이너에서 빈을 가져옴 (IoC)
        UserService userService = context.getBean(UserService.class);

        // UserService의 메소드 호출
        System.out.println(userService.getUser()); // 출력: User1
    }
}
</code></pre>
<ul>
<li>UserRepository와 UserService는 각각 @Repository, @Service 어노테이션을 통해 스프링 컨테이너에 싱글톤 빈으로 등록됩니다.</li>
<li>UserService는 UserRepository를 의존성으로 받는데, 생성자에 @Autowired를 사용하여 스프링이 자동으로 UserRepository 객체를 주입(DI)합니다.</li>
<li>Application 클래스에서 ApplicationContext를 통해 UserService 빈을 가져오는데, 이는 IoC 컨테이너가 빈을 관리하고 제공하기 때문입니다.</li>
<li>이러한 방식으로 스프링은 싱글톤 레지스트리로 빈을 관리하고, IoC를 통해 객체의 생명 주기를 관리하며, DI를 통해 의존성을 주입하여 유연하고 재사용 가능한 코드를 작성할 수 있도록 합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[토비의 스프링 1.3장 DAO의 확장]]></title>
            <link>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.3%EC%9E%A5-DAO%EC%9D%98-%ED%99%95%EC%9E%A5</link>
            <guid>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.3%EC%9E%A5-DAO%EC%9D%98-%ED%99%95%EC%9E%A5</guid>
            <pubDate>Wed, 11 Sep 2024 14:59:44 GMT</pubDate>
            <description><![CDATA[<h3 id="클래스의-분리">클래스의 분리</h3>
<p>UserDao 클래스에서 데이터베이스 연결 기능을 분리해 SimpleConnectionMaker라는 새로운 클래스를 만들고, 그 안에 DB 연결 로직을 넣었습니다.
각 메서드에서 매번 SimpleConnectionMaker 객체를 생성할 수도 있지만, 성능과 효율성을 위해 한 번만 객체를 만들어 재사용하는 것이 낫습니다.</p>
<h3 id="문제-발생">문제 발생</h3>
<p>SimpleConnectionMaker라는 클래스에 직접 의존하고 있는 UserDao의 기존 코드입니다. 
이 코드는 특정 클래스에 종속되어 있어서 DB 연결 로직을 변경하려면 UserDao를 수정해야 하는 문제를 가지고 있습니다.</p>
<p>UserDao 클래스를 N사와 D사에서 상속을 통해 DB 연결 기능을 확장하여 사용하고 있었지만, 이제는 상속을 통한 확장이 어려워졌습니다.</p>
<pre><code class="language-java">public class UserDao {
    private SimpleConnectionMaker connectionMaker;

    public UserDao() {
        // 특정 클래스에 종속됨
        this.connectionMaker = new SimpleConnectionMaker();
    }

    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = connectionMaker.makeConnection();
        PreparedStatement ps = 
        c.prepareStatement(&quot;
        INSERT INTO users(id, name, password) VALUES (?, ?, ?)&quot;);
        ps.setString(1, user.getId());
        ps.setString(2, user.getName());
        ps.setString(3, user.getPassword());

        ps.executeUpdate();
        ps.close();
        c.close();
    }

    public User get(String id) throws ClassNotFoundException, SQLException {
        Connection c = connectionMaker.makeConnection();
        PreparedStatement ps = c.prepareStatement(
        &quot;SELECT * FROM users WHERE id = ?&quot;);
        ps.setString(1, id);

        ResultSet rs = ps.executeQuery();
        rs.next();

        User user = new User();
        user.setId(rs.getString(&quot;id&quot;));
        user.setName(rs.getString(&quot;name&quot;));
        user.setPassword(rs.getString(&quot;password&quot;));

        rs.close();
        ps.close();
        c.close();

        return user;
    }
}
</code></pre>
<p>이 코드의 문제는 UserDao가 SimpleConnectionMaker 클래스에 강하게 결합되어 있어, 다른 DB 연결 클래스로 변경하기가 어렵다는 점입니다.</p>
<h3 id="문제-해결을-위한-접근">문제 해결을 위한 접근</h3>
<p>이 문제를 해결하기 위해 ConnectionMaker라는 인터페이스를 도입하여 SimpleConnectionMaker를 분리하고, UserDao는 이 인터페이스에 의존하도록 수정합니다.</p>
<h3 id="인터페이스-도입">인터페이스 도입</h3>
<p>이 문제를 해결하기 위해 인터페이스를 도입했습니다. 
SimpleConnectionMaker 대신 ConnectionMaker라는 인터페이스를 정의하고, DB 연결을 담당하는 클래스는 이 인터페이스를 구현하게 했습니다.
이렇게 함으로써 UserDao는 더 이상 특정 클래스에 의존하지 않고, 다양한 방식으로 DB 연결 기능을 확장할 수 있게 됩니다.</p>
<h4 id="1-connectionmaker-인터페이스-정의">(1) ConnectionMaker 인터페이스 정의</h4>
<pre><code class="language-java">public interface ConnectionMaker {
    Connection makeConnection() throws ClassNotFoundException, SQLException;
}</code></pre>
<h4 id="2-simpleconnectionmaker-클래스">(2) SimpleConnectionMaker 클래스</h4>
<pre><code class="language-java">public class SimpleConnectionMaker implements ConnectionMaker {
    @Override
    public Connection makeConnection() throws ClassNotFoundException, SQLException {
        Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);
        return DriverManager.getConnection(
        &quot;jdbc:mysql://localhost:3306/mydb&quot;, &quot;username&quot;, &quot;password&quot;);
    }
}</code></pre>
<h4 id="3-userdao-클래스---인터페이스에-의존하도록-수정">(3) UserDao 클래스 - 인터페이스에 의존하도록 수정</h4>
<pre><code class="language-java">public class UserDao {
    private ConnectionMaker connectionMaker;

    // 생성자를 통해 의존성을 주입받음
    public UserDao(ConnectionMaker connectionMaker) {
        this.connectionMaker = connectionMaker;
    }

    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = connectionMaker.makeConnection();
        PreparedStatement ps = c.prepareStatement(
        &quot;INSERT INTO users(id, name, password) VALUES (?, ?, ?)&quot;);
        ps.setString(1, user.getId());
        ps.setString(2, user.getName());
        ps.setString(3, user.getPassword());

        ps.executeUpdate();
        ps.close();
        c.close();
    }

    public User get(String id) throws ClassNotFoundException, SQLException {
        Connection c = connectionMaker.makeConnection();
        PreparedStatement ps = c.prepareStatement(
        &quot;SELECT * FROM users WHERE id = ?&quot;);
        ps.setString(1, id);

        ResultSet rs = ps.executeQuery();
        rs.next();

        User user = new User();
        user.setId(rs.getString(&quot;id&quot;));
        user.setName(rs.getString(&quot;name&quot;));
        user.setPassword(rs.getString(&quot;password&quot;));

        rs.close();
        ps.close();
        c.close();

        return user;
    }
}</code></pre>
<h3 id="인터페이스-적용-결과">인터페이스 적용 결과</h3>
<p>ConnectionMaker라는 인터페이스를 도입함으로써 UserDao는 데이터베이스 연결 방식을 유연하게 변경할 수 있게 되었습니다.
이를 통해 확장성과 유지보수성을 크게 향상시킬 수 있습니다.</p>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) throws ClassNotFoundException
    , SQLException {
        ConnectionMaker connectionMaker = new SimpleConnectionMaker();
        UserDao userDao = new UserDao(connectionMaker); // 인터페이스를 통해 의존성 주입

        User user = new User();
        user.setId(&quot;user1&quot;);
        user.setName(&quot;John Doe&quot;);
        user.setPassword(&quot;password&quot;);

        userDao.add(user);

        User retrievedUser = userDao.get(&quot;user1&quot;);
        System.out.println(&quot;Retrieved User: &quot; + retrievedUser.getName());
    }
}</code></pre>
<blockquote>
<p>이제 UserDao는 ConnectionMaker라는 <strong>인터페이스에 의존</strong>하므로, DB 연결 로직을 변경하고 싶다면 ConnectionMaker 인터페이스를 구현한 새로운 클래스를 만들어 사용하면 됩니다. 
예를 들어 DCompanyConnectionMaker, NCompanyConnectionMaker 같은 클래스를 추가해도 UserDao 코드를 수정할 필요가 없습니다.
<strong>UserDao는 더 이상 특정 클래스에 종속되지 않으며, DB 연결 방식을 변경할 때 코드 수정 없이 인터페이스만 구현하면 됩니다.</strong></p>
</blockquote>
<h3 id="관계-설정-책임의-분리">관계 설정 책임의 분리</h3>
<p>또 다른 중요한 개념은 관계 설정 책임의 분리입니다. 객체 간의 의존 관계를 누가 설정할 것인지의 문제를 해결하는 것이 IoC(제어의 역전)와 DI(의존성 주입)의 핵심입니다.</p>
<h3 id="solid-원칙">SOLID 원칙</h3>
<p>여기서 중요한 설계 원칙인 SOLID 원칙도 언급됩니다.
SOLID 원칙은 다음과 같습니다:</p>
<h4 id="1-단일-책임-원칙-single-responsibility-principle-srp">1. 단일 책임 원칙 (Single Responsibility Principle, SRP)</h4>
<ul>
<li>클래스는 하나의 책임만 가져야 하며, 단 하나의 변경 이유만 가져야 합니다.</li>
<li>클래스가 여러 책임을 가지면, 수정할 때 여러 이유로 수정이 필요할 수 있어 코드 유지보수가 어려워집니다. 각 클래스는 하나의 역할에 집중해야 합니다.</li>
</ul>
<p><strong>예시</strong>: UserDao 클래스는 데이터베이스 관련 로직만 처리해야 하며, 다른 로직(예: 비즈니스 로직이나 파일 처리 등)은 다른 클래스로 분리해야 합니다.</p>
<pre><code class="language-java">public class UserDao {
    // 데이터베이스 접근에 관한 책임만 가짐
    public void add(User user) {
        // DB에 사용자 추가
    }

    public User get(String id) {
        // DB에서 사용자 조회
        return user;
    }
}</code></pre>
<h4 id="2-개방-폐쇄-원칙-openclosed-principle-ocp">2. 개방-폐쇄 원칙 (Open/Closed Principle, OCP)</h4>
<ul>
<li>소프트웨어 요소는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 합니다.</li>
<li>새로운 기능을 추가할 때 기존 코드를 수정하지 않고, 코드를 확장할 수 있어야 합니다.</li>
<li>기존 코드를 변경하지 않고도 기능을 확장할 수 있는 구조로 설계하는 것이 중요합니다.</li>
</ul>
<p><strong>예시</strong>: UserDao가 인터페이스인 ConnectionMaker에 의존하여 DB 연결 방식을 변경할 때 기존 코드를 수정하지 않고도 새로운 DB 연결 방식을 확장할 수 있습니다.</p>
<pre><code class="language-java">public interface ConnectionMaker {
    Connection makeConnection();
}

public class SimpleConnectionMaker implements ConnectionMaker {
    public Connection makeConnection() {
        // DB 연결 로직
        return new Connection();
    }
}

public class UserDao {
    private ConnectionMaker connectionMaker;

    public UserDao(ConnectionMaker connectionMaker) {
        this.connectionMaker = connectionMaker;
    }

    public void add(User user) {
        Connection c = connectionMaker.makeConnection();
        // 사용자 추가
    }
}</code></pre>
<h4 id="3-리스코프-치환-원칙-liskov-substitution-principle-lsp">3. 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)</h4>
<ul>
<li>서브클래스는 언제나 자신의 부모 클래스를 대체할 수 있어야 합니다.</li>
<li>자식 클래스가 부모 클래스의 기능을 덮어쓸 때, 부모 클래스의 기능과 호환되어야 하며, 기존의 기능이 변하지 않아야 합니다. 그렇지 않으면 코드의 예측 불가능성이 높아집니다.</li>
</ul>
<p><strong>예시</strong>: ConnectionMaker 인터페이스를 구현하는 클래스는 모두 ConnectionMaker 타입으로 교체될 수 있어야 합니다.
SimpleConnectionMaker 대신 NCompanyConnectionMaker가 와도 UserDao는 정상적으로 동작해야 합니다.</p>
<pre><code class="language-java">public class NCompanyConnectionMaker implements ConnectionMaker {
    @Override
    public Connection makeConnection() {
        // 다른 방식의 DB 연결 로직
        return new Connection();
    }
}</code></pre>
<h4 id="4-인터페이스-분리-원칙-interface-segregation-principle-isp">4. 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)</h4>
<ul>
<li>특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫습니다. 즉, 인터페이스는 특정 목적에 맞게 분리되어야 합니다.</li>
<li>클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리해야 합니다. 이를 통해 불필요한 의존성을 줄이고, 필요한 기능만 인터페이스에서 제공할 수 있습니다.</li>
</ul>
<p><strong>예시</strong>: ConnectionMaker 인터페이스는 오직 DB 연결에 관련된 책임만 가지며, 다른 기능은 다른 인터페이스로 분리할 수 있습니다.</p>
<pre><code class="language-java">public interface ConnectionMaker {
    Connection makeConnection();
}

public interface TransactionManager {
    void startTransaction();
    void commit();
}</code></pre>
<h4 id="5-의존성-역전-원칙-dependency-inversion-principle-dip">5. 의존성 역전 원칙 (Dependency Inversion Principle, DIP)</h4>
<ul>
<li>고수준 모듈(정책 결정)은 저수준 모듈(세부 구현)에 의존해서는 안 되며, 둘 다 추상화에 의존해야 합니다.</li>
<li>구체적인 클래스가 아닌 인터페이스나 추상 클래스에 의존하도록 설계해야 합니다.</li>
<li>구체적인 구현에 의존하지 않고, 추상화된 인터페이스에 의존하게 함으로써 유연하고 확장 가능한 설계를 만들 수 있습니다.</li>
</ul>
<p><strong>예시</strong>: UserDao는 ConnectionMaker라는 인터페이스에 의존하며, 구체적인 구현(SimpleConnectionMaker, NCompanyConnectionMaker)에 의존하지 않음으로써 DB 연결 방식이 바뀌어도 UserDao 코드를 수정하지 않고 확장할 수 있습니다.</p>
<pre><code class="language-java">public class UserDao {
    private ConnectionMaker connectionMaker;

    public UserDao(ConnectionMaker connectionMaker) {
        this.connectionMaker = connectionMaker;  // 추상화에 의존
    }

    public void add(User user) {
        Connection c = connectionMaker.makeConnection();  // 구체적인 구현에 의존하지 않음
        // 사용자 추가 로직
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[토비의 스프링 1.2장 DAO의 분리]]></title>
            <link>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.2%EC%9E%A5-DAO%EC%9D%98-%EB%B6%84%EB%A6%AC</link>
            <guid>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.2%EC%9E%A5-DAO%EC%9D%98-%EB%B6%84%EB%A6%AC</guid>
            <pubDate>Wed, 11 Sep 2024 14:29:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="userdao-에-팩토리-메소드-패턴을-적용해서-getconnection을-분리합시다">UserDao 에 팩토리 메소드 패턴을 적용해서 getConnection()을 분리합시다!!!</h3>
</blockquote>
<p>UserDao 클래스의 핵심 기능은 데이터베이스에 연결하고 사용자 정보를 처리하는 것입니다.
그런데 데이터베이스 연결(getConnection()) 부분을 팩토리 메소드 패턴을 적용해 분리해보겠습니다.</p>
<pre><code class="language-java">public class UserDao {
    // 팩토리 메소드 패턴을 사용하여 데이터베이스 연결을 담당하는 getConnection() 메서드를 분리
    public Connection getConnection() throws ClassNotFoundException, SQLException {
        // 하위 클래스에서 구현하도록 남겨둠
        return null;
    }

    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = getConnection();
        // SQL 구문 실행 및 자원 해제
        PreparedStatement ps = 
        c.prepareStatement(
        &quot;INSERT INTO users(id, name, password) VALUES (?, ?, ?)&quot;);
        ps.setString(1, user.getId());
        ps.setString(2, user.getName());
        ps.setString(3, user.getPassword());

        ps.executeUpdate();
        ps.close();
        c.close();
    }

    public User get(String id) throws ClassNotFoundException, SQLException {
        Connection c = getConnection();
        PreparedStatement ps = 
        c.prepareStatement(
        &quot;SELECT * FROM users WHERE id = ?&quot;);
        ps.setString(1, id);

        ResultSet rs = ps.executeQuery();
        rs.next();

        User user = new User();
        user.setId(rs.getString(&quot;id&quot;));
        user.setName(rs.getString(&quot;name&quot;));
        user.setPassword(rs.getString(&quot;password&quot;));

        rs.close();
        ps.close();
        c.close();

        return user;
    }
}</code></pre>
<blockquote>
<p>위의 코드에서 getConnection() 메서드는 UserDao 클래스에서 추상화된 메서드로 존재하며, 데이터베이스 연결 방식을 하위 클래스에서 구체화할 수 있도록 분리해두었습니다.</p>
</blockquote>
<h3 id="userdao를-상속한-구체적인-구현-클래스">UserDao를 상속한 구체적인 구현 클래스</h3>
<p>이제 데이터베이스에 대한 구체적인 연결을 담당하는 하위 클래스를 작성해보겠습니다.
예를 들어 MySQL 데이터베이스 연결을 담당하는 MySqlUserDao 클래스입니다.</p>
<pre><code class="language-java">public class MySqlUserDao extends UserDao {
    @Override
    public Connection getConnection() throws ClassNotFoundException, SQLException {
        // MySQL 데이터베이스에 연결하는 구체적인 로직
        Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);
        return DriverManager.getConnection(
        &quot;jdbc:mysql://localhost:3306/mydb&quot;, &quot;username&quot;, &quot;password&quot;);
    }
}</code></pre>
<blockquote>
<p>이렇게 하면 UserDao 클래스의 데이터베이스 연결 부분이 팩토리 메소드 패턴을 통해 분리되고, 상속받은 클래스에서 구체적인 데이터베이스 연결을 처리하게 됩니다.</p>
</blockquote>
<h3 id="팩토리-메소드-패턴-">팩토리 메소드 패턴 ?</h3>
<p><strong>팩토리 메소드 패턴(Factory Method Pattern)</strong>은 객체 생성 로직을 서브클래스로 위임하는 디자인 패턴입니다. 즉, 상위 클래스에서는 객체 생성을 위한 인터페이스만 정의하고, 실제 객체 생성 과정은 서브클래스에서 구현합니다. 이를 통해 상위 클래스는 객체 생성 방식에 대해 알 필요 없이 동일한 방식으로 객체를 사용할 수 있게 됩니다.</p>
<p>특징</p>
<ul>
<li>상위 클래스는 객체 생성의 책임을 지지 않고, 구체적인 클래스에서 이를 해결합니다.</li>
<li>객체 생성 코드를 별도의 서브클래스로 캡슐화함으로써 코드의 유연성과 확장성을 높입니다.</li>
</ul>
<p>장점</p>
<ul>
<li>객체 생성 로직을 캡슐화할 수 있어 코드의 재사용성을 높입니다.</li>
<li>새로운 객체 생성 방식을 추가할 때 상위 클래스를 수정할 필요 없이 서브클래스만 수정하면 됩니다.</li>
</ul>
<h3 id="템플릿-메소드-패턴-">템플릿 메소드 패턴 ?</h3>
<p><strong>템플릿 메소드 패턴(Template Method Pattern)</strong>은 상위 클래스에서 공통적인 로직의 뼈대를 정의하고, 그 중 일부를 하위 클래스에서 구현하도록 하는 디자인 패턴입니다. 즉, 상위 클래스에서 알고리즘의 구조를 정의하고, 일부 단계는 서브클래스에서 구체적으로 구현되도록 하는 방식입니다.</p>
<p>특징</p>
<ul>
<li>알고리즘의 전체적인 흐름을 상위 클래스에서 관리하되, 구체적인 구현은 서브클래스에서 정의합니다.</li>
<li>코드 중복을 최소화하면서, 서브클래스에서 필요한 부분만 수정할 수 있습니다.</li>
</ul>
<p>장점</p>
<ul>
<li>코드의 재사용성을 높이고, 동일한 로직의 구조를 유지하면서 세부 사항만 변경할 수 있습니다.</li>
<li>알고리즘의 변경이 필요한 경우, 상위 클래스를 수정할 필요 없이 하위 클래스만 수정하면 됩니다.</li>
</ul>
<p>템플릿 메소드 패턴 예시</p>
<pre><code class="language-java">public abstract class AbstractUserDao {
    // 템플릿 메소드
    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = getConnection();
        PreparedStatement ps = c.prepareStatement(
        &quot;INSERT INTO users(id, name, password) VALUES (?, ?, ?)&quot;);
        ps.setString(1, user.getId());
        ps.setString(2, user.getName());
        ps.setString(3, user.getPassword());

        ps.executeUpdate();
        ps.close();
        c.close();
    }

    // 하위 클래스에서 구현할 메서드
    protected abstract Connection getConnection() 
                throws ClassNotFoundException, SQLException;
}</code></pre>
<p>위 코드에서 getConnection() 메서드는 템플릿 메소드 패턴에 따라 하위 클래스에서 구체적으로 구현됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토비의 스프링 1.1장 '초난감 DAO의 교훈']]></title>
            <link>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.1%EC%9E%A5-%EC%B4%88%EB%82%9C%EA%B0%90-DAO%EC%9D%98-%EA%B5%90%ED%9B%88</link>
            <guid>https://velog.io/@u_soi/%ED%86%A0%EB%B9%84%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-1.1%EC%9E%A5-%EC%B4%88%EB%82%9C%EA%B0%90-DAO%EC%9D%98-%EA%B5%90%ED%9B%88</guid>
            <pubDate>Thu, 05 Sep 2024 19:01:41 GMT</pubDate>
            <description><![CDATA[<h3 id="1-dao란-무엇인가">1. DAO란 무엇인가!</h3>
<h4 id="data-access-object-데이터에-대한-접근을-추상화하는-방법이다">Data Access Object, 데이터에 대한 접근을 추상화하는 방법이다.</h4>
<h3 id="장점은">장점은?</h3>
<blockquote>
<p>데이터베이스 액세스 로직을 비즈니스 로직에서 분리하기 때문에 코드가 더 깔끔하고 모듈화된다.
유지보수하기 용이하고, 생산성을 높인다.</p>
</blockquote>
<p>리팩토링 전 소스 코드 (책의 예제와는 다른 코드)
<em>DB 커넥션 정보가 변경되면 수십 수백개의 메서드를 일일히 변경해주어야한다.</em></p>
<pre><code class="language-java">public User findUserById(int id) {
    Connection conn = DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/db&quot;);
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(&quot;SELECT * FROM users WHERE id=&quot; + id);

    User user = new User();
    while (rs.next()) {
        user.setId(rs.getInt(&quot;id&quot;));
        user.setName(rs.getString(&quot;name&quot;));
    }
    return user;
}</code></pre>
<p>리팩토링 후 소스 코드
<em>메서드 추출을 통해 재사용 가능한 함수로 분해한다. 이렇게 하니 코드가 더 읽기 쉬워질 뿐만 아니라 테스트하고 유지 관리하기도 더 쉬워지게 된다.</em></p>
<pre><code class="language-java">public User findUserById(int id) {
    Connection conn = getConnection();
    ResultSet rs = executeQuery(conn, &quot;SELECT * FROM users WHERE id=&quot; + id);
    return mapResultSetToUser(rs);
}

private Connection getConnection() {
    // DB 커넥션 생성
    return DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/db&quot;);
}

private ResultSet executeQuery(Connection conn, String query) {
    // SQL 쿼리 실행
    Statement stmt = conn.createStatement();
    return stmt.executeQuery(query);
}

private User mapResultSetToUser(ResultSet rs) {
    // 결과 매핑
    User user = new User();
    if (rs.next()) {
        user.setId(rs.getInt(&quot;id&quot;));
        user.setName(rs.getString(&quot;name&quot;));
    }
    return user;
}</code></pre>
<p>결과적으로 데이터 접근 계층과 비즈니스 로직을 분리하여 결합도를 낮추고 코드의 유지 관리가 더 용이하도록 한다.</p>
<blockquote>
<p>그런데 만일 여러 고객사에서 서로 다른 데이터베이스 연결을 필요로 한다면 어떻게 해야할까?</p>
</blockquote>
<p><strong>추상 클래스에서 공통 기능을 정의하고 하위 클래스가 세부 사항을 구현하도록 하면 코드가 재사용 가능하고 확장 가능하도록 할 수 있다.</strong></p>
<h4 id="abstractdao-추상-클래스">AbstractDAO 추상 클래스</h4>
<pre><code class="language-java">import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public abstract class AbstractDAO {

    protected abstract Connection getConnection() throws SQLException;

    public abstract void save(Object obj);

    public abstract Object findById(int id);
}</code></pre>
<h4 id="mysql-데이터베이스-연결을-구현한-하위-클래스">MySQL 데이터베이스 연결을 구현한 하위 클래스</h4>
<pre><code class="language-java">public class MySQLUserDAO extends AbstractDAO {

    @Override
    protected Connection getConnection() throws SQLException {
        return DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/mydb&quot;
        , &quot;username&quot;, &quot;password&quot;);
    }

    @Override
    public void save(Object obj) {
        User user = (User) obj;
        try (Connection conn = getConnection()) {
            PreparedStatement ps 
            = conn.prepareStatement(&quot;INSERT INTO users (name, email)
            VALUES (?, ?)&quot;);
            ps.setString(1, user.getName());
            ps.setString(2, user.getEmail());
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object findById(int id) {
        return null;
    }
}</code></pre>
<h4 id="oracle-데이터베이스-연결을-구현한-하위-클래스">Oracle 데이터베이스 연결을 구현한 하위 클래스</h4>
<pre><code class="language-java">public class OracleProductDAO extends AbstractDAO {

    @Override
    protected Connection getConnection() throws SQLException {
        return DriverManager.getConnection(&quot;jdbc:oracle:thin:@localhost:1521:orcl&quot;
        , &quot;username&quot;, &quot;password&quot;);
    }

    @Override
    public void save(Object obj) {
        Product product = (Product) obj;
        try (Connection conn = getConnection()) {
            PreparedStatement ps = conn.prepareStatement
                (&quot;INSERT INTO products (name, price)
                VALUES (?, ?)&quot;);
            ps.setString(1, product.getName());
            ps.setDouble(2, product.getPrice());
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object findById(int id) {
        return null;
    }
}</code></pre>
<p><em>이러한 방식은 하위 클래스에 유연성을 제공하는 동시에 공통 논리를 중앙 집중화하여 코드 재사용과 유지 관리를 효율적으로 할 수 있다.</em></p>
<h3 id="기--억--하--자">기 . 억 . 하 . 자</h3>
<p>확장 가능하고 유지 관리 가능한 애플리케이션을 구축하기 위해서는 DAO 패턴, 관심사 분리, 메서드 추출, 추상 클래스 기반 상속과 같은 핵심 개념을 잘 적용해야 한다.</p>
<p>우려 사항을 분리하고 이러한 원칙을 따르면 비즈니스 요구 사항이 증가함에 따라 이해하고 수정하고 확장하기 쉬운 코드베이스가 만들어질 것이다.</p>
]]></description>
        </item>
    </channel>
</rss>