본문 바로가기
개발관련/Spring(BE)

Spring Boot - POJO, JavaBean, Spring Bean

by yjoo_ 2023. 11. 1.

이번 포스팅엔 Bean이라는 것에 대해 공부해보자.

 

자주 사용하는 용어니까 좀 정확하게 이해를 하고 넘어가야겠다.

 

Bean??

Bean이란 흔히 자바에서 사용하는 객체를 말하는 것이라고 한다.

 

그렇다면 모든 객체는 Bean일까? 아니다.

 

Bean이란 IOC 컨테이너(혹은 Spring 컨테이너 같은 걸 지칭한다)에서 관리하는 객체를 뜻하거나

 

Java에서 관리하는 객체를 Bean이라고 한다.

 

여기서 객체는 POJO, JavaBean, SpringBean으로 나뉘는데 하나씩 알아보자.

 

POJO(Plain Old Java Object)

POJO는 Plain Old Java Object라고 하여 말 그대로 가장 오래된 자바의 객체다.

 

모든 자바로 생성하는 객체를 POJO라고 칭한다. 모든 자바 객체는 POJO다.

 

이전 시간까지 우리가 만들어왔던 클래스 인스턴스들은 모두 객체이며 POJO이다.

 

예시)

class Pojo {
    private String text;
    private int number;
    
    public String toString() {
    	return text + ":" + number;
    }
}

Java Bean

자바에서 관리하는 객체인 Java Bean은 조건을 만족해야한다.

  1. public no-arg constructor
  2. getter와 setter가 존재해야함
  3. implement Serializable 포함

매개변수가 없는 public 생성자가 존재해야 하고, getter와 setter가 있어야한다.

getter와 setter가 무엇이냐

클래스 멤버 변수가 private형으로 클래스 내부에서만 접근이 가능해야 하며

 

변수의 값을 가져오기 위해서 getter 메서드를, 값을 수정하기 위해서 setter 메서드를 선언하는 것을 말한다.

 

마지막으로 Serializable은 뭐길래?

우리가 개발을 하다보면 객체를 파일로 저장하거나, 서버로 전송해야할 때가 있다.

 

이 때 Serializable 인터페이스로 구현되어있으면 JVM에서 파일로 저장하거나 받거나 할 수 있다.

 

지금은 알아봤자 큰 의미가 없으니 이정도만 알아두자.

 

Java Bean을 코드 예시로 읽어보자

class JavaBean implements Serializable {
    public JavaBean(){
    }
    
    private String text;
    
    public getText(){
        return text;
    }
    public setText(String text){
        this.text = text;
    }
}

이런 식으로 선언된 자바 객체를 JavaBean이라고 부른다.

 

하지만 위의 개념들은 더이상 중요하지 않다. Java Bean을 사용하는 사람이 많지 않기 때문이다.

 

그렇다면 Spring Bean은 무엇일까?

Spring Bean

사실 Spring Bean이라고해서 크게 다를 것은 없다.

 

스프링 컨테이너(또는 IoC컨테이너)에서 관리하는 모든 객체는 Spring Bean이 된다.

 

Spring Bean을 관리하는 방법은 Bean Factory와 Application Context 두가지가 있는데

 

기초적인건 Bean Factory다. Appliation Context는 Bean Factory를 상속하여 만든 개념이기 때문에

 

오늘 날에는 Bean Factory는 거의 사용되지 않는다고 한다.

Bean Factory

스프링 공식문서에 따르면 특별한 이유가 없다면 사용하지 않는 형식이다.

 

Application Context가 빈 팩토리를 그대로 구현하고 있고 그와 동시에 추가 기능을 제공하기 때문이다.

 

추가 기능들을 사용하는 것보다, 메모리가 중요한 상황이 아니라면 쓸 이유가 없다.

 

그저 가장 기본적인 스프링 컨테이너이자 클래스를 뜻한다.

Application Context

위에서도 설명했듯 빈 팩토리의 기능(빈을 생성하고 관계를 설정)과

 

제어하고 총괄하는 추가 기능을 가진 스프링 컨테이너를 뜻한다.

 

왜 스프링 컨테이너로 객체를 관리하는 걸까?

우리가 API 서비스를 관리한다고 가정해보자.

 

여러 명의 사용자가 API 요청을 보내게 된다면 어떻게 될까?

 

매번 new 연산자를 통해 고객 별로 객체를 생성해 return값을 보내주게 될 것이다.

 

만약 요청 한번당 10개의 객체가 생성되고 초당 50번의 요청이 온다면 어떻게 될까?

 

초당 500개의 객체가 생성될 것이다. 서버 입장에선 무리가 갈 수 밖에 없다.

 

그래서 이러한 현상을 해결하기 위해 싱글톤 이라는 디자인 패턴을 사용하게 된다.

싱글톤?

싱글톤이란 메모리에 단 하나의 객체를 생성하고 이 객체를 통해 데이터를 주고받는 것을 말한다.

 

게임이나 서버에서 흔히 쓰이는 방식이며, 요청 한번당 1개의 객체 생성이기 때문에 서버에도 큰 무리가 없다.

 

결론은

스프링 컨테이너는 이러한 싱글톤 패턴을 이용한 컨테이너기 때문에 이를 통해 객체를 관리하려는 것이다.

 

자 그럼 어떻게 쓰는지 간단하게 알아보자.

 

지난번과 같이 메인 패키지에서 새로운 클래스를 생성하자.

이번 클래스 명은 App02HelloWorldSpring으로 한다.

그리고 psvm 단축어를 사용해 main메서드를 생성한다.

그 다음 클래스를 하나 더 생성해주어야 한다.

Bean값을 가진 Configuration클래스를 생성해준다.

package com.learnspringframework.learnspringframework;

import org.springframework.context.annotation.Configuration;

@Configuration
public class HelloWorldConfiguration {
}

그리고 다음과 같이 @Configuration 어노테이션을 등록해주면

 

해당 클래스가 1개 이상의 빈 메서드를 가지고 있음을 나타내준다.

 

main에서 해당 빈을 사용하기 위해 객체를 선언해준다.

 

package com.learnspringframework.learnspringframework;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App02HelloWorldSpring {
    public static void main(String[] args) {
        var context = new AnnotationConfigApplicationContext(HelloWorldConfiguration.class);
    }
}

좀 길긴한데 AnnotationConfigApplicationContext 객체에 방금 생성한 Configuration 클래스를 넘겨주면 된다.

 

Try with resources

뜬금없이 이건 뭐냐 궁금할 수 있는데, 자바의 문법중 하나다.

 

어플리케이션이 종료될 때 자원을 자동으로 반납하는 구문이다.

 

java.lang.AutoCloseable 인터페이스를 구현하는 객체가 사용 가능하다.

 

확인하는 방법은 간단하다 맨 뒤에 .close()로 메서드가 있는지 확인하고 존재하면 사용이 가능한 구문이다.

package com.learnspringframework.learnspringframework;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App02HelloWorldSpring {
    public static void main(String[] args) {
        try(var context =
                    new AnnotationConfigApplicationContext
                            (HelloWorldConfiguration.class)) {
            //ToDo...
        }
    }
}

이렇게 try문 안에 리소스 객체를 넣기만 하면 끝.

 

자 이제 Bean 객체를 선언하고 사용해보자

 

기본적인 Bean의 선언과 호출은 다음과 같다.

 

@Bean 어노테이션을 객체를 반환하는 메서드에 적용한다

package com.learnspringframework.learnspringframework;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HelloWorldConfiguration {
    @Bean
    public String name(){
        return "yjoo";
    }
}

이 Bean을 호출하기 위해선 getBean 메서드를 사용하면 된다.

package com.learnspringframework.learnspringframework;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App02HelloWorldSpring {
    public static void main(String[] args) {
        try(var context =
                    new AnnotationConfigApplicationContext
                            (HelloWorldConfiguration.class)) {
            System.out.println(context.getBean("name"));
        }
    }
}

프로그램을 Ctrl + Shift + F10으로 실행해 결과 값을 보자.

각 Bean 메서드 값은 겹칠 수 없음을 주의하자.

 

Bean의 이름은 필요에 따라 사용자 지정도 가능하다.

일단 코드를 보자

package com.learnspringframework.learnspringframework;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HelloWorldConfiguration {
    @Bean
    public String name(){
        return "Ravi";
    }

    @Bean(name = "yjoo")
    public String name2(){
        return "yjoo";
    }
}

Bean 어노테이션 옆에 name을 지정해주면 된다.

 

호출 방법은 동일하다. getBean("yjoo");

 

헷갈리지 않도록 위 메서드의 리턴 값도 바꿔주자.

 

실행 결과

물론 이러한 방식은 권장되지 않는다. 가장 좋은 것은 메서드 명이 겹치지 않는 것이다.

 

이 것 외에도 다양한 접근 방법이 있으나 차차 알아가보도록 하자.

마무리

이렇게 Bean을 사용함으로써 Spring 컨테이너에 객체를 생성하는 역할을 맡길 수 있다.

 

무슨 뜻이냐면 객체 생성과 관리의 권한을 개발자가 아닌 Spring에게 온전히 맡기게 된다는 것이다.

 

물론 이것보다 객체들의 의존관계를 스프링이 관리하게 하려는 것 목적이 더 크다.

 

Bean 대해서는 이것 외에도 많은 내용이 있지만, 지금은 이정도로 정리하고 후에 추가하도록 하자