1. 핵심 개념
어노테이션/자바 기반 자동 등록은 개발자가 일일이 자바 코드로 new 객체를 생성하고 조립하는 일련의 과정을 스프링 컨테이너에 완전히 위임하는 방식. 이를 가능하게 하는 두 축이 바로 @ComponentScan과 @Autowired이다.
- 개념: 클래스 위에 @Component 어노테이션을 붙이면, 스프링이 애플리케이션 구동 시 해당 클래스를 찾아내어(Component Scan) 자동으로 스프링 빈에 등록하는 방식.
- @Component (대상의 표시): 클래스 위에 붙이며, 스프링에게 이 클래스는 네가 관리해야 할 빈(Bean)라고 마킹하는 역할. 대상이 클래스 자체.
- @ComponentScan (탐색): 스프링이 설정 정보가 없어도, 지정한 패키지 경로를 샅샅이 뒤져서 @Component가 붙은 클래스를 찾아 스프링 컨테이너에 빈으로 자동 등록하게 만듦.
- @Autowired (조립/의존관계 주입):등록된 빈들(필드나 생성자) 사이에서 필요한 의존성(다른 타입/객체)을 스프링이 스스로 찾아내어 자동으로 연결(주입)해 줌.
ℹ️ 이전의 방식 (수동 등록): XML이나 자바 설정 클래스(@Configuration)에 일일이 @Bean을 적어야 함.
💡 어노테이션에 대해서 (스프링)
1) 스프링에서 어노테이션은 왜 사용할까?
- 어노테이션이 도입되기 전(과거 스프링 등)에는 프로그램의 설정을 거대한 XML 파일에 일일이 적어야 했음. 코드가 조금만 바뀌어도 XML을 같이 수정해야 해서 관리가 매우 힘듦.
- 어노테이션을 사용하면 소스코드와 설정을 한곳에서 직관적으로 관리할 수 있게 됨. 코드의 양도 획기적으로 줄어들고 가독성도 좋아짐.
🔍 어노테이션 (Annotation) 이란?
코드의 본문(로직)을 해치지 않으면서, 코드에 부가적인 정보(메타데이터)를 첨부하여 컴파일러나 프로그램이 특정 처리를 하도록 돕는 접근 방식. 일반 주석과 달리 프로그램(컴파일러, 스프링 컨테이너 등)이 읽고 기능을 수행하는 주석형 명령 또는 라벨. (해당하는 코드의 정체성이나 추가 기능을 명시)
파이썬 (Python) — 데코레이터 (Decorator)
자바스크립트 / 타입스크립트 — 데코레이터
C# — 어트리뷰트 (Attribute)
2) 어노테이션의 구성 및 정의 방법 (메타데이터)
그렇다면 이 어노테이션들은 내부적으로 어떻게 구성되어 있고, 스프링은 어떻게 이를 알아채는 걸까?
- 표준 사양 준수: 스프링은 자바 표준 사양인 JSR-250 (@PostConstruct, @PreDestroy) 이나 JSR-330 (@Inject, @Named) 을 활용하거나 스프링 고유의 기능을 사용해 어노테이션을 구현.
- 커스텀과 메타 정보: 자체적인 커스텀 어노테이션을 만들 때는 내부에 속성(예: `String[] value() default {};`)을 정의할 수 있으며, 정의부에는 반드시 메타 어노테이션을 명시해야 함.
3) 어노테이션의 생명주기 / 유지 기한 (Retention Policy)
어노테이션은 어디까지 유지되느냐에 따라 크게 3가지로 나뉜다.
- 소스 단계 (SOURCE): 컴파일할 때만 사용되고 사라짐. (예: 개발자의 실수를 막아주는 @Override)
- 컴파일 단계 (CLASS): 컴파일된 .class 파일까지는 남아있지만, 프로그램이 실행될 때는 메모리에 로드되지 않음.
- 런타임 단계 (RUNTIME): 프로그램이 실행 중일 때도 계속 유지됨. 스프링 프레임워크는 실행 중에 이 어노테이션을 감지(리플렉션 기법)하여 가동 중에 필요한 기능을 자동으로 연결하고 주입해 줌. (대부분의 스프링 어노테이션이 여기에 속한다.)
2. 계층별 어노테이션 종류
단순히 @Component만 쓰는 것이 아니라, 스프링은 *아키텍처 계층(Layer)에 따라 가독성과 부가 기능을 더한 비즈니스 스테레오타입 어노테이션을 제공한다 (명확하게 역할을 지정하는 것). 이 어노테이션들 내부에는 모두 @Component가 포함되어 있어 자동으로 컴포넌트 스캔의 대상이 됨.
| 주요 역할 및 특징 | 부가 기능 | |
| @Component | 가장 기본이 되는 컴포넌트 스캔 대상 | 없음 (기본 마킹) |
| @Controller / @RestController | 웹 MVC의 컨트롤러(계층)로 인식 | 스프링 MVC 웹 요청 라우팅 처리 |
| @Service | 비즈니스 로직이 처리되는 서비스 계층 파악 | 특별한 부가 기능은 없으나, 핵심 비즈니스 위치를 명시 |
| @Repository | 데이터 접근 계층(DAO 또는 스프링 데이터 JPA) | 데이터 계층의 예외를 스프링의 추상화된 예외로 변환 |
| @Configuration | 스프링 설정 정보로 인식 | 싱글톤(Singleton) 유지를 위한 CGLIB 프록시 기술 적용 |
💡 3대 아키텍처 계층과 전용 어노테이션
1) 프레젠테이션 계층 (Presentation Layer)
- 역할: 사용자의 요청(웹 브라우저의 클릭, API 호출 등)을 맨 처음 맞이하고, 처리 결과를 화면이나 데이터(JSON)로 돌려주는 계층.
- 전용 어노테이션: @Controller 또는 @RestController
- 부가 기능: 웹의 URL 요청을 특정 메서드에 매핑(@RequestMapping)하고, HTTP 상태 코드를 관리하는 웹 관련 특화 기능이 제공.
2) 비즈니스 논리 계층 (Business Logic Layer / Service Layer)
- 역할: 핵심 비즈니스 루틴과 요구사항을 처리하는 계층. (예: "로그인 시 비밀번호가 맞는지 확인하고, 마일리지를 10포인트 적립한다" 같은 실제 서비스 규칙이 실행되는 곳.)
- 전용 어노테이션: @Service
- 부가 기능: 특별한 부가 기능보다는 "여기가 핵심 비즈니스 로직이 모여있는 곳이다"라는 것을 개발자에게 명확히 알려주는 가독성 측면의 역할이 큼. (주로 트랜잭션(@Transactional) 처리가 이 계층에서 시작.)
3) 데이터 접근 계층 (Data Access Layer / Repository Layer)
- 역할: 데이터베이스(DB)나 파일 등 데이터 저장소에 직접 접근하여 데이터를 넣고, 빼고, 수정하는 일을 담당하는 계층.
- 전용 어노테이션: @Repository
- 부가 기능: 데이터베이스를 다치게 할 수 있는 예외(SQLException 등)가 발생했을 때, 이를 스프링이 이해할 수 있는 공통 예외(DataAccessException)로 자동 변환해 주는 아주 중요한 부가 기능이 작동.
✂️ 요약 (사용자가 웹 페이지에서 버튼을 누르면)
- @Controller (프레젠테이션 계층)가 요청을 받아서
- @Service (비즈니스 계층)에게 핵심 처리를 맡기고
- @Repository (데이터 접근 계층)를 통해 DB에서 데이터를 꺼내온다.
💡 함께 쓰이는 어노테이션
1) 의존성 주입 관련 어노테이션
- @Primary: 만약 주입해야 할 같은 타입의 객체가 2개 이상이라 스프링이 헷갈려 한다면, 이 어노테이션이 붙은 걸 최우선으로 선택
- @Qualifier("이름") : 같은 타입의 객체가 여러 개 있을 때, 헷갈리지 않게 지정한 '이름'을 가진 객체를 콕 집어서 주입
- @PostConstruct : 객체가 생성되고 의존성 주입이 완료된 직후에, 이 메서드를 가장 먼저 자동으로 실행 (자바 표준 사양인 JSR-250)
2) 메타 어노테이션
- @Target: 어노테이션이 적용될 위치(메서드, 필드, 클래스 등)를 제한.
- @Retention: 어노테이션 정보가 언제까지 유지될지(컴파일 시점인지, 런타임 시점인 RetentionPolicy.RUNTIME인지)를 결정. 스프링이 가동 중에 리플렉션으로 어노테이션을 감지하려면 런타임 유지(`RetentionPolicy.RUNTIME`)가 필수적임.
3. 자동 등록 및 의존관계 주입의 동작 과정
스프링 애플리케이션이 실행될 때 내부적으로 다음과 같은 순서로 진행됨.
- 빈 정의 메타데이터 수집 (@ComponentScan): 스프링은 메인 클래스가 위치한 패키지와 그 하위 패키지를 전부 뒤져 스캔 대상(@Component)을 탐색. 스프링 부트의 시작점인 @SpringBootApplication 내부에는 @ComponentScan이 기본적으로 포함되어 있음.
- 빈 이름 생성 및 등록: 스프링은 @Component가 붙은 클래스의 인스턴스를 생성하여 스프링 컨테이너에 등록.
- 빈 이름 규칙: 클래스 이름의 첫 글자만 소문자로 바꾼 것을 기본 이름으로 사용한다.
- 예: MemberServiceImpl 클래스 ➡️ memberServiceImpl 이라는 이름의 빈으로 등록.
- 이름을 수동 지정하고 싶다면 @Component("myService") 형태로 작성도 가능
- 빈 이름 규칙: 클래스 이름의 첫 글자만 소문자로 바꾼 것을 기본 이름으로 사용한다.
- 관계 조립 (@Autowired): 생성된 빈들의 코드를 보며 생성자나 필드에 @Autowired가 붙어 있다면, 스프링 컨테이너에서 해당 "타입"과 일치하는 다른 빈을 찾아와서 쏙 넣어줌.
@Service // 1. 컴포넌트 스캔 대상으로 등록 (이름: orderServiceImpl)
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
@Autowired // 2. 컨테이너에 등록된 빈 중 'MemberRepository' 타입을 찾아 자동 주입
public OrderServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
ℹ️ 어노테이션 쌓인 것에 실행 순서가 있을까?
결론부터 말하면, 클래스나 메서드 위에 어노테이션을 여러 개 쌓아 올릴 때 어노테이션 작성 순서(위/아래) 자체는 자바 컴파일러 및 스프링의 동작에 영향을 주지 않음. 동작의 순서를 결정하는 것은 작성된 물리적 순서가 아니라, 각 어노테이션이 속한 스프링의 생명주기(Lifecycle) 단계와 메타 어노테이션의 구조.
1) 상속과 메타 어노테이션의 흐름
- @Configuration 내부(Configuration 어노테이션 인터페이스 정의)를 열어보면 실제로 @Component가 붙어 있음(@Component 기능을 상속받음). 따라서 스프링은 컴파일러가 위에서 아래로 읽는 순서와 상관없이, 해당 어노테이션이 어떤 메타 어노테이션을 품고 있는지 계층 구조를 파악하여 빈 등록 대상으로 삼음.
2) 스프링 내부 처리 프로세서의 순서
스프링은 빈을 만들 때 어노테이션을 읽는 체크리스트 단계(포스트 프로세서)가 정해져 있음.
- 인스턴스 생성 단계: @Autowired나 @Inject가 붙은 생성자를 보고 의존성을 먼저 해결.
- 값 주입 단계: 인스턴스가 힙에 뜨고 나면 필드나 세터에 붙은 @Value를 보고 PropertyEditor를 돌려 값을 꽂아 넣음.
- 초기화 단계: 주입이 완벽히 끝난 후 최종적으로 빈의 @PostConstruct 메서드를 호출.
따라서 코드를 작성할 때 어떤 것을 먼저 위에 쓰든 상관없으며, 스프링이 설계된 생명주기 엔진 순서대로 안전하게 처리된다. 가독성을 위해 팀 내에서 일관된 스타일(예: Spring 관련 어노테이션을 위로, 부가 기능이나 검증을 아래로)을 맞춰 작성하는 것을 추천.
4. 자동 등록 방식의 장단점
👍 장점
- 압도적인 생산성과 편리함: 클래스를 만들고 어노테이션만 붙이면 끝남 (어노테이션 몇 개로 설정이 끝남). 빈 설정 파일(AppConfig)을 열어 매번 코드를 추가하는 번거로움이 전혀 없음.
- 설정 관리의 최소화: 애플리케이션 규모가 커져 수백, 수천 개의 빈이 생겨도 설정 코드가 비대해지지 않음.
👎 단점 및 주의점
- 파편화된 설정 정보: 빈 설정이 각 클래스 파일에 흩어져 있으므로, 현재 애플리케이션에 어떤 빈들이 어떻게 엮여 있는지 전체 구조를 한눈에 파악하기 어려움.
- 수정의 제약: 내가 직접 코드를 고칠 수 없는 외부 라이브러리(Jar) 내의 클래스는 당연히 @Component를 붙일 수 없으므로 자동 등록이 불가능. (이럴 때는 @Configuration 파일에서 자바 코드로 직접 반환하는 수동 등록 방식을 써야 함.)
'Tech Stack > Backend' 카테고리의 다른 글
| 🍃스프링, 자바 표준, 자카르타 세 진영의 어노테이션 비교 (0) | 2026.06.24 |
|---|---|
| 🥾Spring Boot 시작하기 (개발 환경 구축) (0) | 2026.06.23 |
| 🥾스프링 부트 소개: 설정의 늪에서 생산성의 혁명으로 (0) | 2026.06.19 |
| 🍃간단 Spring Core & 아키텍처의 이해 (0) | 2026.06.19 |
| 🍃빌드 시스템(Maven/Gradle) 이해 및 프로젝트 기본 구조 (0) | 2026.06.19 |