저는 스프링 부트로 프로젝트를 하다보면 Bean, Configuration, Context라는 단어를 자주 접했습니다.
접할 때마다 헷갈려서 개념에 대해 간단하게 정리하고자 합니다.
Bean
스프링에서 빈은 스프링 컨테이너에 의해 생성 및 관리되는 객체를 말합니다. 이 빈들은 스프링 애플리케이션에서 재사용 가능한 구성 요소로, 컨테이너에 등록되어 관리됩니다.
Bean은 다음과 같은 특징이 있습니다.
재사용성: 빈들은 재사용 가능한 구성 요소로 설계되어 있으며, 이를 통해 코드의 중복을 줄이고 유지보수성을 향상시킵니다.
의존성 주입: 빈들 간의 의존성은 스프링의 DI (Dependency Injection)를 통해 주입되며, 이는 객체 간의 결합도를 낮추고 테스트 가능한 코드를 작성할 수 있게 합니다.
Configuration
스프링에서 설정은 애플리케이션의 구성 정보를 담은 클래스로, 주로 @Configuration 어노테이션을 사용하여 표시됩니다. 이 클래스에서는 빈들의 정의와 의존성을 설정하고 관리합니다.
Configuration은 다음과 같은 특징이 있습니다.
빈 정의: 설정 클래스에서는 @Bean 어노테이션을 사용하여 빈들을 정의합니다. 이를 통해 스프링 컨테이너에 등록되고 관리됩니다.
외부 설정 연동: 설정 클래스에서는 @ConfigurationProperties 어노테이션을 사용하여 외부 설정 파일의 값을 빈에 주입할 수 있습니다.
조건부 빈 등록: @ConditionalOn... 어노테이션을 사용하여 특정 조건에 따라 빈을 등록할 수 있습니다.
Context
스프링 부트 컨텍스트는 스프링 프레임워크의 핵심 기능인 IoC (Inversion of Control)와 DI (Dependency Injection)를 지원하는 컨테이너입니다. 이 컨텍스트는 애플리케이션의 구성 요소들을 관리하고, 객체들 간의 의존성을 관리하여 애플리케이션을 구성하고 실행하는 역할을 합니다.
Context는 다음과 같은 특징이 있습니다.
빈 관리: 컨텍스트는 빈(bean)이라 불리는 객체들을 생성하고 관리합니다. 빈은 스프링 애플리케이션에서 재사용 가능한 구성 요소로, 컨텍스트는 이러한 빈들을 생성하고 관리합니다.
의존성 주입: 빈들 간의 의존성은 컨텍스트가 관리하며, 이를 통해 객체 간의 결합도를 낮추고 테스트 및 유지보수를 용이하게 합니다.
따라서 일반적인 스프링 애플리케이션에서는 설정 클래스에 @Bean 어노테이션을 사용하여 빈을 정의하고, 이 설정 클래스를 스프링 컨텍스트에 등록하여 빈들이 관리되게 됩니다.
이렇게 하면 컨텍스트가 설정 클래스를 기반으로 빈들을 생성하고 의존성을 주입하게 됩니다.
@Component 외 내가 사용한 어노테이션들은 Bean으로 등록해주는 기능이 있나?
Bean에 대해서 알아보다가 제가 사용했던 @Component, @Repository, @Service, @RestController가 생각이 났습니다.
그래서 Bean의 관점에서 같이 알아보고 간단하게 정리하고자 합니다.
@Component
@Component 어노테이션은 스프링에서 관리하는 빈으로 등록하기 위해 사용되는 가장 기본적인 어노테이션 중 하나입니다.
이 어노테이션을 사용하면 해당 클래스를 스프링의 컨테이너에서 빈으로 인식하게 되어, 의존성 주입과 같은 스프링의 기능을 활용할 수 있습니다.
@Component 어노테이션을 사용하는 클래스는 주로 비즈니스 로직이나 다양한 기능을 수행하는 클래스로서, 스프링 애플리케이션 컨텍스트에 등록되어 다른 클래스에서 이를 주입받아 사용할 수 있습니다.
이 외에도 @Repository, @Service, @Controller 등은 @Component를 확장한 특수한 종류의 어노테이션으로 사용되기 때문에 @Repository,@Service,@Controller를 사용하는 클래스도 빈으로 등록됩니다.
import java.io.*;
import java.util.*;
public class Main {
public static int white = 0;
public static int blue = 0;
public static int[][] board;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
board = new int[N][N];
for (int i=0; i<N; i++) {
st = new StringTokenizer(br.readLine());
for (int j=0; j<N; j++) {
int value = Integer.parseInt(st.nextToken());
board[i][j] = value;
}
}
partition(0, 0, N);
System.out.println(white);
System.out.println(blue);
}
public static boolean isAllOneOrZero(int row, int col, int size) {
int color = board[row][col];
for (int i=row; i<row+size; i++) {
for (int j=col; j<col+size; j++) {
if (board[i][j] != color) {
return false;
}
}
}
return true;
}
public static void partition(int row, int col, int size) {
if (isAllOneOrZero(row, col, size)) {
if (board[row][col] == 0) {
white++;
} else {
blue++;
}
return;
}
size /= 2;
partition(row, col, size);
partition(row+size, col, size);
partition(row, col+size, size);
partition(row+size, col+size, size);
}
}
로직 설명
체크하는 범위 내 모든 원소가 0인지 1인지 확인하는 로직
public static boolean isAllOneOrZero(int row, int col, int size) {
int color = board[row][col];
for (int i=row; i<row+size; i++) {
for (int j=col; j<col+size; j++) {
if (board[i][j] != color) {
return false;
}
}
}
return true;
}
row와 col은 board에서 체크하는 범위의 시작인 행과 열입니다.
size는 체크하는 범위의 길이입니다.
color는 체크하는 범위의 가장 처음 확인하는 색깔입니다.
그 이후 이중 for문으로 체크하는 범위의 모든 원소들이 color와 같은지 확인합니다.
모두 color와 같다면 true, 아니라면 false를 반환합니다.
1/2의 크기로 4개로 분할하는 재귀 로직
public static void partition(int row, int col, int size) {
if (isAllOneOrZero(row, col, size)) {
if (board[row][col] == 0) {
white++;
} else {
blue++;
}
return;
}
size /= 2;
partition(row, col, size);
partition(row+size, col, size);
partition(row, col+size, size);
partition(row+size, col+size, size);
}
row와 col은 board에서 체크하는 범위의 시작인 행과 열입니다.
size는 체크하는 범위의 길이입니다.
isAllOneOrZero 결과가 true라면 색깔에 따라 white 또는 blue에 1을 더해줍니다.