군만두의 IT 공부 일지

[스터디7] 01. 스프링 컨텍스트: 빈 정의 본문

프로그래밍/Java

[스터디7] 01. 스프링 컨텍스트: 빈 정의

mandus 2025. 4. 11. 21:02

목차

     

     

     

     

     

      제목: 스프링 마스터

      저자: 로렌티우 스필카, 정성권 번역

      출판사: 길벗

      가격: 33,000원

     

     

    기초부터 탄탄하게 배워서 바로 쓰는 스프링 입문

     

     

     

     

     

     

    '이것이 자바다' 스터디가 끝나고, 이번에는 스프링 스터디를 진행하려고 한다. 해당 용어에 대해 부족한 점이 많은 것 같아, 여러 책들을 분석한 결과 적절한 구성의 책을 선정하였다.

    2장. 스프링 컨텍스트: 빈 정의

    이 장에서 다룰 내용
    - 스프링 컨텍스트의 필요성 이해하기
    - 스프링 컨텍스트에 새로운 객체 인스턴스 추가하기
    • 컨텍스트: 프레임워크가 관리할 모든 객체 인스턴스를 추가하는 앱의 메모리 공간
    • 스프링이 객체를 볼 수 있게 하려면 컨텍스트에 객체를 추가해야 한다.
    • 객체 인스턴스를 빈(Bean)이라고 한다.

    2.1 메이븐 프로젝트 생성

    • 메이븐(Maven): 사용하는 프레임워크에 관계없이 앱의 빌드 프로세스를 쉽게 관리하는 데 사용하는 도구
    • 빌드 도구: 앱을 더 쉽게 빌드하는 데 사용하는 소프트웨어. 앱 빌드 일부인 작업을 수동으로 수행하는 대신 빌드 도구가 수행하도록 구성한다.
    • 앱 빌드에 포함되는 작업
      • 앱에 필요한 의존성 내려받기
      • 테스트 실행
      • 구문이 정의한 규칙 준수 여부 검증
      • 보안 취약점 확인
      • 앱 컴파일
      • 실행 가능한 아카이브에 앱 패키징
    • 먼저, 통합 개발 환경(Integrated Development Enviroment, IDE)가 필요하다. IntelliJ에서 File > New > Project를 선택하여 새 프로젝트를 생성한다.
      • 그룹 ID(Group ID): 관련된 여러 프로젝트를 그룹화하는 데 사용
      • 아티팩트 ID(Artifact ID): 현재 애플리케이션 이름
      • 버전(Version): 현재 구현 상태의 식별자

    • 프로젝트를 생성하면 다음 사항을 관찰할 수 있다.
      • src 폴더: 소스 폴더라고 하며, 앱에 속한 모든 것을 넣을 수 있다.
        • main 폴더: 애플리케이션의 소스 코드를 저장한다. 이 폴더에는 자바 코드와 구성 정보가 java 및 resources라는 두 하위 폴더에 개별적으로 포함된다.
        • test 폴더: 단위 테스트의 소스 코드를 저장한다.
      • pom.xml 파일: 새 종속성 추가처럼 메이븐 프로젝트 구성을 작성하는 파일이다. 모든 의존성은 <dependencies>와 </dependencies> 태그 사이에 작성한다.

    2.2 스프링 컨텍스트에 새로운 빈 추가

    • 다음 방법으로 컨텍스트 빈을 추가할 수 있다.
      • @Bean 애너테이션 사용
      • 스테레오타입(stereotype) 애너테이션 사용
      • 프로그래밍 방식

    메이븐 프로젝트를 생성하고 클래스를 정의한다. Parrot 클래스를 만들고, 이름을 나타내기 위해 String 속성을 추가한다.

    package main;
    
    public class Parrot {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    그리고 Parrot 클래스 인스턴스를 생성할 수 있는 main 메서드가 포함된 클래스를 정의한다.

    package main;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Main {
    
        public static void main(String[] args) {
            var context = new AnnotationConfigApplicationContext();
    
            Parrot p = new Parrot();
        }
    }

    이제 프로젝트에 필요한 의존성을 추가한다.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>sq-ch2-ex1</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>17</maven.compiler.source>
            <maven.compiler.target>17</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>6.1.6</version>
            </dependency>
        </dependencies>
    
    </project>

    2.2.1 @Bean 애너테이션을 사용하여 스프링 컨텍스트에 빈 추가

    • 스프링에서 빈을 추가하는 방법을 배우는 이유는 스프링이 빈 일부인 객체만 관리할 수 있기 때문이다.
    • @Bean 애너테이션을 사용하여 스프링 컨텍스트에 빈을 추가하는 단계
      1. 프로젝트 구성 클래스를 정의한다. 스프링의 컨텍스트를 구성하는 데 사용할 구성 클래스를 정의한다.
      2. 컨텍스트에 추가하려는 객체 인스턴스를 반환하는 메서드를 구성 클래스에 추가하고 @Bean 애너테이션으로 메서드에 주석을 추가한다.
      3. 스프링이 구성 클래스를 사용하게 한다. 구성 클래스를 사용하여 다양한 프레임워크 구성을 작성한다.
    // 구성 클래스
    package config;
    
    import main.Parrot;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration	// 이 클래스를 스프링 구성 클래스로 정의함
    public class ProjectConfig {
    
        @Bean	// 스프링 컨텍스트가 초기화될 때 이 메서드를 호출하고 반환된 값을 컨텍스트에 추가하라고 지시함
        Parrot parrot() {
            var p = new Parrot();
            p.setName("Koko");
            return p;
        }
    
        @Bean
        String hello() {
            return "Hello";
        }
    
        @Bean
        Integer ten() {
            return 10;
        }
    }
    // 스프링 컨텍스트 초기화
    package main;
    
    import config.ProjectConfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Main {
    
        public static void main(String[] args) {
            var context = new AnnotationConfigApplicationContext(ProjectConfig.class);	// 스프링 컨텍스트 인스턴스가 생성될 때 구성 클래스를 매개변수로 전송하여 스프링이 이를 사용하도록 지시함
    
            Parrot p = context.getBean(Parrot.class);	// 스프링 컨텍스트에서 Parrot 타입의 빈 참조를 가져옴
    
            System.out.println(p.getName());
    
            String s = context.getBean(String.class);
    
            System.out.println(s);
    
            Integer n = context.getBean(Integer.class);
    
            System.out.println(n);
        }
    }
    package main;
    
    public class Parrot {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    2.2.2 스테레오타입 애너테이션으로 스프링 컨텍스트에 빈 추가

    • 스테레오타입 애너테이션을 사용하려면 스프링 컨텍스트에 추가해야 할 인스턴스의 클래스 위에 @Component 애너테이션을 추가해야 한다. 이 방식을 사용하려면 스테레오타입 애너테이션으로 지정된 클래스를 찾을 위치를 스프링에 알려주는 구성(configuration) 클래스가 필요하다.
    • 클래스를 컴포넌트로 표시하는 과정
      1. @Component 애너테이션으로 스프링이 해당 컨텍스트에 인스턴스를 추가할 클래스를 표시한다.
      2. 구성 클래스 위에 @ComponentScan 애너테이션으로 표시한 클래스를 어디에서 찾을 수 있는지 스프링에 지시한다.
    package main;
    
    import org.springframework.stereotype.Component;
    
    @Component // 스프링은 이 클래스의 인스턴스를 생성하고 스프링 컨텍스트에 추가함
    public class Parrot {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    package config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan(basePackages = "main")	// 스프링에 스테레오타입 애너테이션이 지정된  클래스를 찾을 위치를 알려줌
    public class ProjectConfig {
    
    }
    package main;
    
    import config.ProjectConfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Main {
    
        public static void main(String[] args) {
            var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
    
            Parrot p = context.getBean(Parrot.class);
    
            System.out.println(p);
            System.out.println(p.getName());
        }
    }

    스프링 컨텍스트에 빈을 추가하는 두 가지 방법을 비교하면 다음과 같다.

    @Bean 애너테이션 사용 스테레오타입 애너테이션 사용
    1. 스프링 컨텍스트에 추가할 인스턴스의 생성을 완전히 제어할 수 있다. 스프링은 해당 인스턴스만 받아 컨텍스트에 그대로 추가한다.
    2. 이 메서드를 사용하면 동일한 타입의 인스턴스를 스프링 컨텍스트에 더 추가할 수 있다.
    3. @Bean 애너테이션을 사용하여 스프링 컨텍스트에 모든 객체 인스턴스를 추가할 수 있다.
    3. 생성하는 각 빈에 대해 별도의 메서드를 작성해야 하므로 앱에 상용구 코드가 추가된다. 이런 이유로 프로젝트에서 @Bean을 사용하기보다는 스테레오타입 애너테이션을 선호한다.
    1. 프레임워크가 인스턴스를 생성한 후에만 인스턴스를 제어할 수 있다.
    2. 이렇게 하면 컨텍스트에 클래스의 인스턴스를 하나만 추가할 있다.
    3. 스테레오타입 애너테이션은 애플리케이션이 소유한 클래스의 빈을 생성하는 데만 사용할 수 있다.
    4. 스테레오타입 애너테이션을 사용하여 스프링 컨텍스트에 빈을 추가해도 앱에 상용구 코드가 추가되지 않는다.

    2.2.3 프로그래밍 방식으로 스프링 컨텍스트에 빈 추가

    스프링 5에서는 스프링 컨텍스트에 프로그래밍 방식으로 빈을 추가할 수 있다. 컨텍스트에 빈을 추가하는 사용자 재정의 방법을 요구하고 싶지만, @Bean 또는 스테레오타입 애너테이션 방식이 요구에 충족되지 않을 때 사용한다.

    package main;
    
    import config.ProjectConfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.function.Supplier;
    
    public class Main {
    
        public static void main(String[] args) {
            var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
    
            Parrot x = new Parrot();
            x.setName("Kiki");
    
            Supplier<Parrot> parrotSupplier = () -> x;
    
            //context.registerBean("parrot1", Parrot.class, parrotSupplier);
    
            context.registerBean("parrot1",
                    Parrot.class,
                    parrotSupplier,
                    bc -> bc.setPrimary(true));
    
            Parrot p = context.getBean(Parrot.class);
    
            System.out.println(p.getName());
        }
    }
    package main;
    
    public class Parrot {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    package config;
    
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ProjectConfig {
    }

     

    이 글은 『스프링 교과서』 책을 학습한 내용을 정리한 것입니다.
    Comments