inblog logo
|
LifeLog, DevLog
    Spring

    Spring의 주요 강점 - 1, DI

    DI에 관하여
    KYJTHEYJ's avatar
    KYJTHEYJ
    Nov 25, 2025
    Spring의 주요 강점 - 1, DI
    • DI (의존성 주입)

      • 쉬운 예시로 ‘레고 조립하기’

        • 스프링 X

          public class 카페 {
              private 바리스타 바리스타 = new 신입바리스타(); // 고정됨!
          
              public void 커피만들기() {
                  바리스타.에스프레소추출();
              }
          }
          • 만약 신입 바리스타가 퇴사시 → 코드를 직접 고쳐야하는 상황

        • 스프링 O

          @Component
          public class 카페 {
              private final 바리스타 바리스타;
          
              // 스프링이 알아서 바리스타를 찾아서 넣어줌
              public 카페(바리스타 바리스타) {
                  this.바리스타 = 바리스타;
              }
          
              public void 커피만들기() {
                  바리스타.에스프레소추출();
              }
          }
          
          @Component
          class 신입바리스타 implements 바리스타 { }
          
          @Component
          class 경력바리스타 implements 바리스타 { }
          • 스프링이 직접 바리스타를 찾아줌

        • 실제 코드에서의 예시

          • 스프링 X

            // ========================================
            // 1. User 클래스
            // ========================================
            public class User {
                private String name;
                private String email;
                private String password;
            
                public User(String name, String email, String password) {
                    this.name = name;
                    this.email = email;
                    this.password = password;
                }
            }
            
            // ========================================
            // 2. UserRepository (DB 저장)
            // ========================================
            public class UserRepository {
                public void save(User user) {
                    System.out.println("MySQL DB에 저장: " + user.getName());
                    // 실제로는 JDBC 코드 수십 줄...
                }
            }
            
            // ========================================
            // 3. EmailService (이메일 발송)
            // ========================================
            public class EmailService {
                public void sendWelcome(User user) {
                    System.out.println("환영 이메일 발송: " + user.getEmail());
                    // 실제로는 SMTP 코드 수십 줄...
                }
            }
            
            // ========================================
            // 4. UserService (회원가입 로직) ⭐️
            // ========================================
            public class UserService {
            
                // 💥 문제: 직접 생성 → 완전히 고정됨! -> 핵심 문제!
                private UserRepository repository = new UserRepository();
                private EmailService emailService = new EmailService();
            
                public void register(String name, String email, String password) {
                    User user = new User(name, email, password);
                    repository.save(user);
                    emailService.sendWelcome(user);
                }
            }
            
            // ========================================
            // 5. 실제 사용
            // ========================================
            public class Main {
                public static void main(String[] args) {
                    UserService userService = new UserService();
                    userService.register("김철수", "chulsoo@email.com", "1234");
                }
            }
            
            // 출력:
            // MySQL DB에 저장: 김철수
            // 환영 이메일 발송: chulsoo@email.com
            • 주요 문제점

              • UserService에서 직접 객체를 생성하여 고정시켜 놓았으므로
                repository 나 emailService의 결합도가 극도로 높음
                다른 환경의 repository, emailService가 필요하면
                별도의 객체를 생성해서 추가 생성자를 만드는 등의 행위가 필요

              • 테스트 또한 힘듬, 해당 파트의 코드를 다 고쳐야하는 상황

          • 스프링 O

            // ========================================
            // 1. User 클래스 (동일)
            // ========================================
            public class User {
                private String name;
                private String email;
                private String password;
            
                public User(String name, String email, String password) {
                    this.name = name;
                    this.email = email;
                    this.password = password;
                }
            
                // getter/setter...
            }
            
            // ========================================
            // 2. 인터페이스 정의 (규격)
            // ========================================
            interface UserRepository {
                void save(User user);
            }
            
            interface NotificationService {
                void sendWelcome(User user);
            }
            
            // ========================================
            // 3. Repository 구현체들
            // ========================================
            @Repository
            @Primary  // ⭐️ 기본으로 사용할 구현체!
            class MySqlUserRepository implements UserRepository {
                @Override
                public void save(User user) {
                    System.out.println("[MySQL] DB에 저장: " + user.getName());
                }
            }
            
            @Repository
            class PostgresUserRepository implements UserRepository {
                @Override
                public void save(User user) {
                    System.out.println("[PostgreSQL] DB에 저장: " + user.getName());
                }
            }
            
            // ========================================
            // 4. Notification 구현체들
            // ========================================
            @Service
            @Primary  // ⭐️ 기본으로 사용할 구현체!
            class EmailNotificationService implements NotificationService {
                @Override
                public void sendWelcome(User user) {
                    System.out.println("[이메일] 발송: " + user.getEmail());
                }
            }
            
            @Service
            class SmsNotificationService implements NotificationService {
                @Override
                public void sendWelcome(User user) {
                    System.out.println("[SMS] 발송: " + user.getPhone());
                }
            }
            
            // ========================================
            // 5. UserService (회원가입 로직) ⭐️
            // ========================================
            @Service
            public class UserService {
            
                private final UserRepository repository;
                private final NotificationService notificationService;
            
                // ✨ 생성자: 스프링이 자동으로 주입!
                public UserService(UserRepository repository,
                                   NotificationService notificationService) {
                    this.repository = repository;
                    this.notificationService = notificationService;
            
                    // 💡 어떤 구현체가 들어올지 몰라도 됨!
                    // 💡 그냥 규격(인터페이스)만 알면 됨!
                }
            
                public void register(String name, String email, String password) {
                    User user = new User(name, email, password);
                    repository.save(user);  // 어떤 DB인지 몰라도 됨!
                    notificationService.sendWelcome(user);  // 이메일인지 SMS인지 몰라도 됨!
                }
            }
            
            // ========================================
            // 6. 실제 사용
            // ========================================
            @SpringBootApplication
            public class Application {
                public static void main(String[] args) {
                    SpringApplication.run(Application.class, args);
            
                    // 스프링 내부 동작:
                    // 1. MySqlUserRepository 객체 생성 (@Primary)
                    // 2. PostgresUserRepository 객체 생성
                    // 3. EmailNotificationService 객체 생성 (@Primary)
                    // 4. SmsNotificationService 객체 생성
                    // 5. UserService 생성 시:
                    //    → MySqlUserRepository 주입
                    //    → EmailNotificationService 주입
                }
            }
            
            @RestController
            public class UserController {
            
                private final UserService userService;
            
                public UserController(UserService userService) {
                    this.userService = userService;  // 스프링이 주입!
                }
            
                @PostMapping("/register")
                public String register(@RequestBody RegisterRequest request) {
                    userService.register(
                        request.getName(),
                        request.getEmail(),
                        request.getPassword()
                    );
                    return "회원가입 성공!";
                }
            }
            
            // 출력:
            // [MySQL] DB에 저장: 김철수
            // [이메일] 발송: chulsoo@email.com
            • 해결된 문제점

            • 결합도가 극도로 높음
              다른 환경의 repository, emailService가 필요하면
              별도의 객체를 생성해서 추가 생성자를 만드는 등의 행위

              → 아래와 같이 유연함을 지님

              1. UserRepository, NotificationService를 Interface화
              → 규격화
              2. 규격을 받아 내어 필요한 Repository를 선언,
              MySqlUserRepository, PostgreUserRepository 로 구현함
              (NotificationService는 Email, SmsNotificationService로)
              3. UserService에서 생성자는 스프링이 컴파일할 때
              생성자를 자동으로 주입
              해줌, 즉 코드 전체가 아닌 명시만 고치면 됨
              (@Primary or @Profile 등 코드에 명시된 대로)


    • 최종적으로 의존성 주입을 통해 아래와 같은 강점을 지님

      • 객체의 생성을 직접 선언할 필요가 없어짐

      • 의존성 관리를 자동으로 해주고 있어, 핵심 코드 변경이 필요가 없음
        (명시만 바꿔주는 것으로 해결, 결합도 하락, 유지보수 용이)

      • 테스트 환경의 경우, 따로 Profile을 선언하여 이 Profile의 경우 이런 의존성을 사용한다고 명시 가능 (Annotation을 통해, if의 무한 반복 없어짐)

    • 한마디

      • 객체가 필요한 의존성을 직접 생성하는게 아닌 외부에서 주입받는 것

        규격화된 의존성을 외부에서 받아 new 키워드를 통해 고정되지 않으므로

        이를 생성자 파라미터로 받아 결합도를 낮추는 스프링의 특징

      • 여기서 객체 생성을 스프링이 해준다는 것 (IoC Container)
        은 IoC (제어의 역전) 라고 함

    Share article

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog