[TIL] _ 260214 _ 스프링 빈을 배우다 🫛

호두·2026년 2월 16일

✔️ Java / Spring

목록 보기
10/25

스프링 빈과 의존관계

회원 컨트롤러가 회원 서비스와 리포지토리를 사용할 수 있게 의존관계로 설정

보통 라이브러리는 코드를 만드는 사람이 호출해서 사용하지만, 스프링은 반대로 스프링이 주도권을 잡아서 내 코드에 붙은 애노테이션을 보고 라이브러리를 호출해서 실행한다. 그리고 우리는 어떤 라이브러리를 스프링이 가져다 쓰는지 몰라도 된다.
➡️ 제어의 역전 (IoC)추상화

스프링은 실행되면 먼저 스프링 컨테이너 라는 공간을 만든다.



컴포넌트 스캔과 자동 의존관계 설정


컴포넌트 (Component)

소프트웨어 공학에서 컴포넌트(Component)는 "교체가 가능한 독립된 부품"이라는 뜻이다. 스프링은 클래스를 단순한 코드가 아니라, 언제든 갈아 끼울 수 있는 '부품'으로 취급한다. 그래서 그 클래스 위에 시스템의 부품(Component) 도장을 찍어두는 역할을 하는 애노테이션의 한 종류다. (클래스 타입 ❌)

@Controller, @Service, @Repository 이 3개의 애노테이션 안에는 사실 @Component 가 선언되어있다. 그래서 컴포넌트 스캔에 인식될 수 있는 것이다.

확인이 되면 해당 클래스의 객체(스프링 빈)을 생성하고 스프링 컨테이너에 저장한다. 스프링 빈은 위 같은 주요 애노테이션이 붙은 클래스의 객체를 말한다. 스프링 빈은 각각 하나의 객체만 생성할 수 있다.(싱글톤 유지)

package hello.hello_spring.controller;

import hello.hello_spring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller // ✅ 이 애노테이션을 보고 스프링이 스프링빈을 생성해서 스프링 컨테이너에 저장
public class MemberController {

    private final MemberService memberService;

    @Autowired // 연관된 스프링빈을 찾아 연결해줌
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

의존 관계

@Autowired 가 보이면 밑의 코드를 참고해서 스프링(외부)이 스프링 컨테이너에 스프링 빈들을 연결시켜준다. 멤버 컨트롤러는 멤버 서비스에 요청을 알려야하는 일을 수행하기 위해서는 멤버 서비스 객체가 필요하다. 여기서 컨트롤러와 서비스는 의존관계이다. 그리고 이 애노테이션은 스프링 빈을 생성할 수 있는 클래스에서만 선언해 사용할 수 있다.

✔️ 의존관계를 외부에서 넣어주는 것을 의존성 주입 (Dependency Injection, DI) 이라고 한다.
내(타입)가 움직여서 어떤 일을 수행하는게 아니라 외부(스프링)에서 조정을 해주는 방식이다. 이 의존성 주입의 장점은 그 대상과 기능이 분리됐기 때문에 객체가 바뀌더라도 코드를 수정하지 않고 그대로 사용할 수 있어서 코드가 유연하고 재사용성이 높아진다.

DI 의 3가지 방법

외부(e.g. 스프링) 에서 내부의 일을 조정할 때 사용할 수 있는 방법

  1. 필드 주입 : 필드는 객체 내에서 보통 private 으로 설정되서 외부가 내부의 내용을 확인 할 수 없고 너무 연결성이 강해 적절하지 않다.
  2. Setter 호출 : 먼저 객체를 생성 한 뒤에 호출이 되면 조립해주는 방법이다. 언제 조립이 되는지 알 수가 없어 정확도가 떨어진다.
  3. 생성자 호출 : 외부에서 정보를 확인할 수 있고 생성 시 바로 조립되기 때문에 안정적인 방법이다. 이 방법을 보통 사용한다.

  1. 표식 남기기 : @Component 나, 이를 가진 애노테이션을 선언해둔다.
  2. 순찰 : ComponentScan 이 @Component 애노테이션이 붙은 클래스를 다 돌면서 찾는다.
  3. 조립 (DI) : 스프링이 해당 클래스의 객체를 생성해서 연결한다.


자바코드로 스프링 빈 직접 생성

Controller

package hello.hello_spring.controller;

import hello.hello_spring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;

public class MemberController {

    private final MemberService memberService;

    @Autowired 
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

➡️ @Controller 를 선언하지 않았다. (컨포넌트 스캔이 확인해서 생성하지 못함)

스프링 빈 생성 클래스

package hello.hello_spring;

import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.repository.MemoryMemberRepository;
import hello.hello_spring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

// 아직 DB 가 선정이 안되서 변경 가능성이 있는 경우는 이렇게 스프링 컨테이너에 등록한다.
    @Bean
    public MemberRepository memberRepository() { 
        return new MemoryMemberRepository();
    }

}

➡️ 스프링 빈을 생성하는 클래스를 만들고 ✅ @Configuration 을 선언한다. @Configuration은 스프링 빈을 어떻게 만들고 조립할지 적어놓은 설계도라고 표시하는 애노테이션이다.

  • @Component: 부품으로 등록
  • @Configuration: 다른 부품들을 만드는 방법을 선언(중앙통제실 역할)

@Configuration 의 특징 : 싱글톤 유지 ‼️

스프링은 @Configuration이 붙은 클래스를 특별하게 처리한다. memberService()를 호출할 때 내부에서 memberRepository()를 또 호출하는데, 자바 문법대로라면 새로운 리포지토리가 계속 만들어져야 하지만, @Configuration 덕분에 스프링은 이미 만들어져 있는 리포지토리가 있다는 걸 알아채고 있던 걸 사용하도록 관리한다.


Controller 는 왜 직접 스프링 빈을 생성하지 않을까?

스프링에서 컨트롤러는 웹과의 연결을 실행할 때 서비스나 리포지토리와 달리 필수적이다. 웹의 요청을 받고 다시 웹에 전송하는 역할을 이 컨트롤러에서 하기 때문이다. 그래서 스프링은 실행될 때 자동으로 컨트롤러의 객체를 생성해 스프링 컨테이너에 저장하고 시작한다.

0개의 댓글