091

[JAVA/Spring Boot] Spring Framework: IoC, Bean, DI 본문

Programming Language/Java

[JAVA/Spring Boot] Spring Framework: IoC, Bean, DI

공구일 2026. 3. 16. 19:53
728x90

Spring Boot = Spring Framework + Auto Configuration (자동 설정) + 내장 서버 (Tomcat 등) + Starter (의존성 모음)

1. IoC(Inversion of Control)

 

- IoC는 제어의 역전을, 객체 생성과 관리 책임을 개발자가 아닌 프레임워크가 가지는 Spring Framework의 대표적인 특징을 말합니다.

//1️⃣[Spring에서 사용되지 않는 객체 직접 생성]
UserService service = new UserService();

//2️⃣[Spring IoC, 이전 권장 방식인 필드주입]
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
}

//3️⃣[현재 권장 방식인 생성자 주입 + 롬복]
@Service
@RequiredArgsConstructor 
public class UserService {
    
    private final UserRepository userRepository; 
    private final PasswordEncoder passwordEncoder;
}

-> 기존 Java 프로그램에서는 위의 코드1️⃣처럼 직접 개발자가 객체를 생성하는 것이 당연했지만 Spring에서는 객체 생성은 Spring Container가 하는 것을 권장합니다. 이후에 코드2️⃣처럼 작성됐지만, 테스트 코드에서 선언해둔 userRepository를 바꾸기가 어려워 외부에서 조작하기 힘들고, UserService 생성시점에 userRepository가 아직 들어오지 않는, 완전하지 않은 객체 상태가 존재와 final을 사용하지 못하는 불변성 문제가 있었습니다. 이후에 코드3️⃣에서 사용되는 Lombok의 @RequiredArgsConstructor와 생성자 자동 주입 기능이 결합되어 안전하게 DI 처리가 가능해졌기 때문에 코드3️⃣처럼 쓰는 것이 권장됩니다.(즉 UserService 생성 때 userRepositroy를 바로 주입하기 때문에 항상 완전한 상태가 됩니다.)

 

- DI(Dependency Injection)는 의존성을 외부에서 주입하는 구조입니다. DI를 구현하면서 얻을 수 있는 이점이 바로 인터페이스를 통한 느슨한 결합입니다. 개발자가 직접 객체를 생성하게 되면 코드가 특정 기술에 종속되기때문에 나중에 수백 개의 파일에 들어간 객체를 전부 수정해야합니다. DI를 통해 주입하면 그 객체의 특성을 일일히 고려하지 않아도 되는, 부품을 갈아끼는 듯한 대처가 가능합니다.

(ex) DB를 MySQL에서 다른 DB로 변경할 경우 강한 결합으로 생성하고 나면 new MySQL 부분을 전부 수정해야합니다. 하지만 느슨한 결합일 경우에는 어떤 구현체를 주입할 지는 Spring Container가 결정하기 때문에 변경하지 않아도 됩니다.

// 개발자가 직접 생성 (강한 결합)
MovieRepository repository = new MySQLMovieRepository();

// DI를 통한 주입 (느슨한 결합)
private final MovieRepository repository;

 

Q. Repository를 만들 때 생각해보면 class가 아닌 interface로 선언해서 만듭니다. 자바 내에서 인터페이스는 규칙이기 때문에 직접 이를 new 키워드를 사용해서라도 객체로 만들 수가 없습니다. 하지만 서비스 단에서 레포지토리를 위처럼 느슨한 결합 선언을 하여 사용할 수 있는 이유는 뭘까요?

A. 바로 스프링 부트가 켜질 때 스프링은 MemberRepository 인터페이스를 보고 메모리상에 몰래 이 인터페이스를 구현한 가짜 클래스(대리인)을 하나 만들어서 개발자가 사용할 수 있게 해줍니다. 이런 가짜 구현체 객체를 프록시(Proxy)라고 부릅니다.

 

2. Bean

 

- Spring에서는 관리되는 객체들을 Spring Bean이라고 부릅니다.즉, Spring Container가 만들고 관리하는 객체가 Bean입니다. 예전에는 이런 클래스를 XML 같은 설정 파일에 이 클래스를 빈 등록을 명시해줘야했는데 그걸 해결한게 Component Scan입니다. 

- Component Scan이 찾아내는 주요 타켓은 @Component를 뿌리로 두는 모든 어노테이션이며 그중 중요하게 여기서 설명할 어노테이션은 @RestController(@Controller), @Service, @Repository, 총 3가지입니다.

->@RestController는 사용자의 HTTP 요청을 가자 먼저 받고 응답을 돌려줍니다. @Controller와의 차이는 각각 데이터 반환, view 반환으로 생각하면 쉽습니다. (@RestController = @Controller + @ResponseBody)

->@Service는 핵심 비지니스 로직으로, 실질적인 데이터 가공이나 규칙, 연산을 수행하는 곳입니다.

-> @Repository는 데이터베이스 창고 관리자로, 데이터베이스에 접근하여 데이터를 저장, 수정, 삭제, 조회하는 역할을 합니다.

 

Q. @Data나 @Entity는 Component Scan의 대상(Bean)이 아닌가요?
A. 어노테이션이라 헷갈릴 수 있지만 위의 3가지의 경우, 스프링 컨테이너가 관리하는 싱글톤이 아니라, 사용자의 요청이나 DB의 행마다 수시로 생성되고 사라지는 데이터 덩어리입니다. 각각 @Data는 Lombok의 어노테이션, @Entity는 JPA의 어노테이션입니다.

 

- Bean은 Spring Container 생성 -> Bean 생성 -> 의존성 주입(DI) -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> Spring 종료의 생명주기를 갖습니다.

-Bean이 싱글톤으로 관리되는데에는 메모리 효율성과 성능이 가장 큰 이유가 됩니다. 자바 웹 서버의 기본 동작 방식은 Thread per Request로 요청 당 스레드(프로세스안에서 실질적으로 작업을 실행하는 단위)가 처리합니다. 스레드 풀에서 대기하던 스레드가 전담처리를 끝내고 나면 다시 스레드 풀로 돌아갑니다. 이 때 다수의 스레드에서 공유되는 싱글톤 객체인 빈은 Heap 영역에서 공유 자원으로 사용되고 지역변수는 Stack 영역에서 보관됩니다. 

 

Q. 스레드 외에 다른 단위로는 무엇이 있나요?
A. JS에서는 자바처럼 스레드를 다수로 만드는 것이 아닌 싱글 스레드로, 이벤트 루프로 일을 걸어둔 뒤 바로 다음 사람의 요청을 받습니다. Spring에서도 Spring WebFlux라는 기술이 이 방식을 차용했으며 단위는 Stream입니다. 그 외에도 OS 스레드를 생성하고 관리하는 단위를 가볍게 만든 가상 스레드도 있습니다.

 

728x90