스파르타 14일차

임선구·2026년 3월 27일

스파르타

목록 보기
13/28

객체지향적 코딩에 대해서 생각해보았다
코딩을 하면서 처음으로 생각해 본 듯?


생성자 주입이 왜 좋은가?

이전에는 Scanner를 CommerceSystem 안에서 직접 생성할지,
아니면 Main에서 만들어서 넘길지 고민했다.

예를 들면 이런 두 방식이 있다.

1. 클래스 내부에서 직접 생성하는 방식

public class CommerceSystem {

    private Scanner scanner = new Scanner(System.in);

}

2. 생성자로 전달받는 방식

public class CommerceSystem {

    private Scanner scanner;

    public CommerceSystem(Scanner scanner) {
        this.scanner = scanner;
    }
}

이 두 방식 중에서 객체지향적으로 더 깔끔한 방법은
생성자 주입 방식이다.


1. 생성자 주입이란?

생성자 주입은
클래스가 필요한 객체를 스스로 만들지 않고, 밖에서 전달받는 방식이다.

예를 들면:

Scanner scanner = new Scanner(System.in);
CommerceSystem commerceSystem = new CommerceSystem(scanner);

이 구조는 이런 흐름이다.

  1. Main이 필요한 객체를 생성한다.
  2. CommerceSystem은 생성자를 통해 그 객체를 전달받는다.
  3. 전달받은 객체를 필드에 저장해서 사용한다.

즉, CommerceSystem이 Scanner를 직접 만들지 않고
이미 준비된 Scanner를 받는 구조다.


2. 왜 직접 생성하지 않고 전달받는 것이 좋을까?

이유 1. 역할이 더 분명해진다

Main은 프로그램 시작점이고,
필요한 객체를 준비하는 역할을 맡는다.

CommerceSystem은
준비된 객체를 받아서 실제 기능을 수행하는 역할을 맡는다.

즉 역할이 이렇게 나뉜다.

  • Main : 준비 담당
  • CommerceSystem : 실행 담당

이 구조가 더 자연스럽다.

만약 CommerceSystem 안에서 Scanner를 직접 만든다면,
CommerceSystem은 기능 수행뿐 아니라
객체 생성까지 같이 하게 된다.

그러면 역할이 조금 섞인다.


이유 2. 코드가 더 일관적이다

이미 CommerceSystem은 products도 밖에서 받고 있다.

public CommerceSystem(List<Product> products, Scanner scanner) {
    this.products = products;
    this.scanner = scanner;
}

즉 products는 밖에서 받고, scanner만 내부에서 직접 만들면
설계가 통일되지 않는다.

반면 둘 다 밖에서 전달받으면 흐름이 깔끔하다.

  • 필요한 객체는 모두 Main이 준비한다.
  • CommerceSystem은 전달받은 것만 사용한다.

이런 구조가 더 읽기 쉽고 설계 의도가 분명하다.


이유 3. 나중에 변경하기 쉬워진다

처음에는 Scanner(System.in)을 쓸 수 있다.
그런데 나중에는 입력 방식이 달라질 수도 있다.

예를 들어:

  • 파일에서 입력받기
  • 테스트용 입력값 사용하기
  • 다른 입력 객체로 변경하기

만약 CommerceSystem 안에서 직접 new Scanner(System.in)을 써버리면,
입력 방식을 바꾸려면 CommerceSystem 코드를 직접 수정해야 한다.

하지만 생성자 주입 구조라면
CommerceSystem은 그대로 두고,
Main에서 어떤 Scanner를 넘길지만 바꾸면 된다.

즉 변경에 더 유연하다.


이유 4. 테스트하기 쉬워진다

지금 단계에서는 테스트 코드까지 깊게 안 가도 되지만,
구조적으로는 이것도 큰 장점이다.

직접 생성하는 방식은:

private Scanner scanner = new Scanner(System.in);

이렇게 고정돼 있어서 테스트할 때 다루기 불편하다.

반면 생성자 주입은:

public CommerceSystem(Scanner scanner) {
    this.scanner = scanner;
}

이렇게 되어 있으니,
테스트할 때 원하는 입력 객체를 넣어볼 수 있다.

즉 외부에서 조립 가능하다는 점이 장점이다.


3. 직접 생성 방식과 생성자 주입 방식 비교

직접 생성 방식

public class CommerceSystem {

    private Scanner scanner = new Scanner(System.in);

}

이 방식의 특징:

  • 편해 보인다
  • 바로 쓸 수 있다
  • 하지만 클래스가 도구를 직접 만들고 있다
  • 변경과 테스트에 불리하다

생성자 주입 방식

public class CommerceSystem {

    private Scanner scanner;

    public CommerceSystem(Scanner scanner) {
        this.scanner = scanner;
    }
}

이 방식의 특징:

  • 처음엔 조금 더 길어 보인다
  • 하지만 구조가 더 깔끔하다
  • 필요한 객체를 외부에서 조립할 수 있다
  • 역할 분리가 명확하다

4. 현재 프로젝트에 적용하면

Main.java

Scanner scanner = new Scanner(System.in);

CommerceSystem commerceSystem = new CommerceSystem(products, scanner);
commerceSystem.start();

scanner.close();

CommerceSystem.java

private List<Product> products;
private Scanner scanner;

public CommerceSystem(List<Product> products, Scanner scanner) {
    this.products = products;
    this.scanner = scanner;
}

이 구조의 장점은 명확하다.

  • Main이 필요한 객체를 준비한다.
  • CommerceSystem은 전달받은 객체를 사용한다.
  • 설계가 일관적이다.

5. 초보자 눈높이로 이해하면

이걸 아주 쉽게 생각하면:

직접 생성 방식

CommerceSystem이 말한다.

“상품도 내가 받고, Scanner는 내가 직접 만들게.”

생성자 주입 방식

Main이 말한다.

“필요한 재료는 내가 다 준비해줄게.
너는 그걸 받아서 일만 하면 돼.”

두 번째가 더 깔끔하지 않나?

즉 생성자 주입은
필요한 재료를 밖에서 넣어주는 방식이라고 생각하면 된다.


6. 생성자 주입의 핵심 정리

생성자 주입이 좋은 이유는 다음과 같다.

  • 역할 분리가 더 명확하다
  • 객체 생성 책임이 Main에 모인다
  • 설계가 일관적이다
  • 변경에 유연하다
  • 테스트하기 쉽다

7. 내가 이번에 이해한 점

이번 과제를 하면서 느낀 점은,
단순히 “동작만 하면 된다”가 아니라
누가 만들고, 누가 받고, 누가 사용하는지를 나누는 것이 중요하다는 점이었다.

특히 Scanner처럼 작은 객체 하나도
어디서 생성하는지가 설계 차이를 만든다는 걸 알게 됐다.

처음에는 CommerceSystem 안에서 직접 new Scanner(System.in)을 해도 된다고 생각했지만,
생성자 주입 방식으로 바꾸고 나니
코드의 역할이 더 분명해지고 구조도 더 자연스러워졌다.


한 줄 요약

생성자 주입은 클래스가 필요한 객체를 직접 만들지 않고, 밖에서 전달받아 사용하는 방식이며, 역할 분리와 유지보수 측면에서 더 좋은 설계이다.

profile
끝까지 가면 내가 다 이겨

0개의 댓글