Java로 이해하는 IoC와 DI

직접 생성하는 의존성을 생성자로 전달하고 메모리 저장소를 바꾸어 테스트하기

이번 문서에서 만드는 것

5주는 Spring 없이 생성자 주입을 구현합니다. 회원 목록을 사용하는 코드와 저장소 구현을 분리하여 가짜 저장소로 검사합니다. 앞 주 Member와 컬렉션을 이해해야 합니다.

IoC에서는 객체를 사용하는 쪽이 생성·연결까지 맡지 않고 외부로 제어를 넘깁니다. DI는 필요한 객체를 외부에서 전달하는 방법입니다. 애너테이션이 없어도 DI를 할 수 있습니다. 구현이 하나뿐인 모든 클래스에 인터페이스를 추가하라는 뜻은 아닙니다. 여기서는 저장소 교체를 학습하기 위해 작은 경계를 만듭니다.

세 파일로 의존성 연결하기

di 폴더에 Member.java를 복사합니다. MemberRepository.java:

Java
import java.util.List;
public interface MemberRepository {    List<Member> findAll();}

MemoryMemberRepository.java:

Java
import java.util.List;
public class MemoryMemberRepository implements MemberRepository {    private final List<Member> members;
    public MemoryMemberRepository(List<Member> members) {        this.members = List.copyOf(members);    }
    @Override    public List<Member> findAll() { return members; }}

MemberService.java:

Java
import java.util.List;import java.util.Objects;
public class MemberService {    private final MemberRepository repository;
    public MemberService(MemberRepository repository) {        this.repository = Objects.requireNonNull(repository);    }
    public List<String> names() {        return repository.findAll().stream().map(Member::getName).toList();    }}

List.copyOf는 목록의 구조 변경을 막는 복사이며 Member를 깊게 복제하지 않습니다. 이 저장소는 읽기 예제입니다. Service 안에서 new MemoryMemberRepository(...)를 호출하지 않으므로 Service는 저장 방식에 직접 묶이지 않습니다.

조립하는 곳과 사용하는 곳

Main.java에서 객체를 조립합니다.

Java
import java.util.List;
public class Main {    public static void main(String[] args) {        MemberRepository repository = new MemoryMemberRepository(            List.of(new Member(1, "가람"), new Member(2, "나래"))        );        MemberService service = new MemberService(repository);        System.out.println(service.names());
        MemberRepository emptyRepository = List::of;        MemberService emptyService = new MemberService(emptyRepository);        if (!emptyService.names().isEmpty()) throw new AssertionError("빈 명단 처리 실패");        System.out.println("가짜 저장소 검사 통과");    }}
Shell
javac -encoding UTF-8 Member.java MemberRepository.java MemoryMemberRepository.java MemberService.java Main.javajava Main

List::of는 인자가 없는 findAll에 빈 목록을 반환하는 작은 구현입니다. 이 문법이 낯설면 new MemberRepository() { public List<Member> findAll() { return List.of(); } }로 작성해도 같습니다.

Spring에서 달라질 부분

지금은 main이 생성자에 객체를 넣습니다. 다음 주에는 Spring이 등록된 Bean을 찾아 생성자에 전달합니다. Service의 규칙 자체를 바꾸는 것은 아닙니다. 의존성이 빠졌거나 같은 타입 후보가 여러 개면 시작 단계에서 연결 오류를 만날 수 있습니다. 필드를 public으로 열거나 임의의 static 전역 객체로 우회하지 말고 등록과 주입을 확인합니다.

세션 실습과 완료 기준

권장 90분: 직접 new와 주입 비교 20분, 파일 구현 25분, 가짜 저장소 검사 25분, 설명 20분입니다.

  1. 가짜 저장소가 회원 한 명을 반환하도록 바꿉니다. Service 소스를 수정하지 않고 이름 결과가 달라져야 합니다.
  2. repository를 null로 전달해 생성 시점에 실패하는 것을 확인합니다.
  3. 의존성 화살표를 Main → Service → Repository 인터페이스 ← 구현으로 그립니다.

완료 기준은 DI를 “Spring 애너테이션” 없이 설명하고 구현 교체 시 수정되는 파일과 유지되는 파일을 구분하는 것입니다. 제출물은 두 저장소 실행 결과와 의존성 그림입니다.

더 읽어보기

연결된 PBL 미션과 VOD

주차·미션참고 VOD 범위
5주 · IoC와 DIJava 첫걸음 8·9장

강좌 안내: 참고 강좌 1. 이 문서는 영상 전체를 옮긴 전사 자료가 아닙니다. 세부 내용은 위 공식 문서에서 확인합니다.