본문 바로가기
Backend/Spring

Spring : Spring의 기초에 대해서

by 코딩쥐 2024. 10. 23.

스프링 프레임워크는 자바 기반의 프레임워크로, 주요 특징으로는  AoP (관점지향 프로그래밍, Aspect Oriented Programming), POJO(Plain Old Java Object), IoC(제어의 역행,  Inversion of Control), DI(의존성 주입, Dependency injection)를 들 수 있다.

 

AoP

공통으로 사용하는 기능들을 외부의 독립된 클래스로 분리하고, 해당 기능을 프로그램 코드에 직접 명시하지 않고 후에 선언하여 적용하는 것을 말한다.

 

POJO

POJO는 복잡한 객체 모델이나 프레임워크에 의존하지 않고, 순수한 자바 클래스 형태를 유지하는 객체를 지칭한다. 스프링 프레임워크는 POJO를 사용하여 객체를 관리하므로, 특별한 규칙이나 제약 없이도 객체를 쉽게 정의하고 사용할 수 있다. 이로 인해 코드가 더 가볍고 유지 보수가 용이해진다. 

 

IoC

스프링 프레임워크에서는 객체의 생성과 관리를 컨테이너(특정 객체의 생성과 관리를 담당하며 객체 운용에 필요한 다양한 기능을 제공)가 대신 처리 한다. 스프링 컨테이너의 종류에는 BeanFactory와 ApplicationContext 두 가지 유형의 컨테이너가 있다. 

  • BeanFactory
    가장 기본적인 IoC 컨테이너로, 객체의 생성과 관리에 대한 최소한의 기능만을 제공하고, 필요할 때만 <bean>객체가 생성이 되기 때문에 일반적으로 많이 사용되지 않는다.
  • ApplicationContext 
    BeanFactory의 기능을 확장한 컨테이너로, 다양한 추가 기능을 제공한다. 해당 컨테이너가 구동되는 시점에 <bean> 등록된 클래스들을 즉시 로딩하는 방식으로 동작한다. 
    • <bean>은 스프링 컨테이너에 등록된 객체로, 스프링에서 빈의 생성, 초기화, 의존성 주입 등을 통해 재사용 가능하다. @bean 어노테이션을 사용하면 beanpool(bean을 저장하는 공간)에 저장되어 스프링에서 객체를 관리하고, 그 속성을 설정할 수 있다.

 

DI

객체 간의 의존성을 관리하는 디자인 패턴으로, 구성 요소 간의 의존 관계를 외부에서 설정을 통해 정의하는 방식이다. 스프링에서는 생성자, setter, 인터페이스 등을 통해서 주입한다. IoC와 함께 사용된다.


의존성 관리 예제

스프링 프레임워크는 객체의 생성과 의존관계를 컨테이너가 자동으로 관리(IoC)한다. 자동으로 관리할 때 사용하는 방법이 의존성 주입(DI)이다. Setter 메서드를 기반으로 하는 Setter Injection과 생성자를 기반으로 하는 Constructor Injection으로 나뉜다. 

  • @Configuration
    Configuration 어노테이션은 해당 클래스가 스프링 설정클래스 임을 나타낸다. 스프링 컨테이너가 해당 클래스를 읽고, 이 클래스 내의 메서드들을 통해 빈을 정의하고 설정한다.
     
  • @Bean
    Bean 어노테이션은 메서드가 반환하는 객체를 스프링의 빈으로 등록하겠다는 뜻이다.

1. 빈으로 생성 할 클래스를 생성한다.

먼저 빈으로 생성할 클래스를 정의한다. 해당 예제에서는 Eat 클래스를 생성하여 음식 이름을 저장하고 반환하는 기능을 구현한다.

package com.example.project01.beans;

public class Eat {
    //멤버변수 선언
    private String food;

    //생성자 선언
    public Eat(String food){
        this.food = food;
    }

    //getter, setter 메서드 선언
    public String getFood(){
        return food+"를 먹는다.";
    }

    public void setFood(String food) {
        this.food = food;
    }
}

 

2. Configuration 설정 클래스 설정

Eat 객체를 빈으로 등록 할 설정 클래스를 생성한다. @Configuration과 @Bean 어노테이션을 사용한다. eat 메서드는 Eat 객체를 생성(인스턴스 생성)하고 스프링 컨테이너에 빈으로 등록하며, 기본 값을 "기본 음식"으로 설정한다. Eat 인스턴스를 생성하여 Eat 클래스의 속성과 메서드를 사용할 수 있다.

package com.example.project01.beans;

//Bean을 import 받는다.
import org.springframework.context.annotation.Bean;
//Configuration을 import 받는다.
import org.springframework.context.annotation.Configuration;

//스프링 설정 클래스 임을 나타낸다.
@Configuration
public class BeanConfigClass {
    //해당 메서드 객체를 스프링의 빈으로 등록한다.
    @Bean
    public Eat eat(){
        //기본 값을 설정한다.
        return new Eat("기본 음식");
    }
}

 

3. 애플리케이션에서 빈 사용

Eat 클래스를 사용하여 스프링 애플리케이션의 메인 클래스를 작성한다. 스프링 컨테이너에 직접 접근하여 "eat"이라는 이름으로 등록된 Eat 빈(Eat.class)을 가져온다. Eat 클래스의 메서드와 속성을 사용할 수 있으며, setter메서드를 통해서 값을 설정하고 getter메서드를 통해서 값을 가져올 수 있다. 

package com.example.project01;

//Eat 클래스를 import한다.
import com.example.project01.beans.Eat;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// ApplicationContext를 import한다.
import org.springframework.context.ApplicationContext;

//스프링 부트 애플리케이션의 시작점
@SpringBootApplication
public class Project01Application {

	public static void main(String[] args) {
		//스프링 컨테이너에 직접 접근할 수 있도록 app이라는 변수를 할당한다.
		ApplicationContext app = SpringApplication.run(Project01Application.class, args);
		Eat apple = app.getBean("eat", Eat.class);
		// 빈의 속성을 업데이트할 수 있다
		apple.setFood("사과");
		System.out.println(apple.getFood());
	}
}


Spring에서 Bean을 기본으로 등록할 때, 아무 설정 없이 등록하면 싱글톤 스코프가 적용된다. 즉 Bean을 여러번 호출 해도 항상 같은 인스턴스를 반환한다. 아래와 같이 apple과 grape를 통해 Eat 인스턴스를 생성했을 때 둘의 주소가 동일한 것을 볼 수 있다.

@SpringBootApplication
public class Project01Application {

	public static void main(String[] args) {
		ApplicationContext app = SpringApplication.run(Project01Application.class, args);
		Eat apple = app.getBean("eat", Eat.class);
		Eat grape = app.getBean("eat", Eat.class);
		System.out.println(apple == grape);
	}
}

 

주소가 다른 Eat 인스턴스를 반환하고 싶을 경우에는 프로토타입 스코프를 사용한다. 프로토타입 스코프는 Bean을 호출할 때마다 매번 새로운 인스턴스를 생성한다. 

  • @Scope("prototype") 
package com.example.project01.beans;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//Scope를 import 받는다.
import org.springframework.context.annotation.Scope;

@Configuration
public class BeanConfigClass {
    @Bean
    // Scope에 prototype을 지정한다. 
    @Scope("prototype")
    public Eat eat(){
        return new Eat("기본 음식");
    }
}

 

@Scope("prototype")을 적용한 후, apple == grape를 실행하면 false가 출력되는 모습을 볼 수 있다.