본문으로 건너뛰기
김신건의 로그

[Spring] Stereotype 어노테이션: @Component, @Service, @Repository, @Controller

· 수정 · 📖 약 2분 · 897자/단어 #spring #bean #stereotype #component-scan
@Component, @Service, @Repository, @Controller, @RestController, spring stereotype, 스프링 스테레오타입, @ComponentScan

정의

Stereotype 어노테이션 은 클래스가 Spring 컨테이너의 어떤 역할 (role) 인지를 의미적으로 표시한다. 모두 @Component메타 어노테이션 으로 가진 specialization.

어노테이션의미부가 기능
@Component일반 Spring 관리 Bean컴포넌트 스캔 대상화
@Service비즈니스 로직 계층의미적 분류만 (기능 동일)
@RepositoryDAO / 영속성 계층PersistenceExceptionTranslator 가 SQLException 을 Spring DataAccessException 으로 변환
@ControllerWeb MVC 계층 (view 반환)DispatcherServlet 이 handler 로 인식
@RestControllerREST API (JSON 반환)@Controller + @ResponseBody 합성
@Configuration@Bean 메서드를 담는 설정 클래스CGLIB proxy 로 @Bean 메서드 호출을 가로채 singleton 보장

기능적으로 @Component 와 동일한 게 대부분이지만 검색 / advisor 적용 / 자동 처리 의 단위가 된다.

컴포넌트 스캔

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {}

com.example 하위 패키지를 재귀 스캔해 stereotype 이 붙은 클래스를 자동 Bean 등록.

@SpringBootApplication 은 내부적으로 @ComponentScan 을 포함, 자신이 선언된 패키지를 root 로 스캔. 그래서 main 클래스를 top-level package 에 두는 게 관례.

@SpringBootApplication   // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

각 어노테이션 상세

@Component

가장 기본. 의미적 분류가 명확하지 않을 때 사용.

@Component
public class JsonMapperHelper { ... }

@Service

비즈니스 로직. 트랜잭션이 시작되는 layer 라는 의미. 기술적으로는 @Component 와 동일하지만 읽는 사람에게 의미 전달.

@Service
public class OrderService {
    @Transactional
    public Order place(Cart cart) { ... }
}

@Repository

DAO. 자동 예외 변환 이 진짜 기능:

@Repository
public class UserDao {
    @PersistenceContext
    EntityManager em;

    public User findById(Long id) {
        return em.find(User.class, id);
        // 만약 SQLException 이 던져지면 자동으로 DataAccessException 으로 변환
    }
}

PersistenceExceptionTranslationPostProcessor 가 BeanPostProcessor 로서 @Repository 가 붙은 Bean 의 메서드 호출을 가로챈다. 가로채는 예외:

  • SQLExceptionDataAccessException 계층
  • JPA PersistenceException → 동일
  • Hibernate 예외도 변환

Spring Data JPA 의 JpaRepository 인터페이스 구현체도 자동으로 @Repository 처리됨.

java
@Repository
class UserDao {
  @PersistenceContext EntityManager em;
  public User findById(Long id) {
      return em.find(User.class, id);  // 만약 SQLException 이 발생하면
  }
}

// main: id 가 존재하지 않을 때
try {
  userDao.findById(999L);
} catch (DataAccessException e) {
  System.out.println(e.getClass().getSimpleName());
}
stdout
EmptyResultDataAccessException

@Repository 가 없으면 native JpaException 이 그대로 전파됨.

@Controller

Spring MVC handler. DispatcherServlet 의 HandlerMapping@Controller Bean 의 @RequestMapping 메서드를 매핑.

@Controller
public class HomeController {
    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("name", "World");
        return "home";   // view 이름 → ViewResolver 가 .html 또는 .jsp 로 변환
    }
}

return type 이 String 이면 view 이름. ModelAndView 도 가능.

@RestController

REST API 용. @Controller + @ResponseBody 의 합성. 모든 메서드 return 값이 자동으로 HTTP body 로 직렬화 (JSON).

@RestController
@RequestMapping("/api/users")
public class UserApiController {
    @GetMapping("/{id}")
    public User get(@PathVariable Long id) {
        return userService.findById(id);   // JSON 으로 직렬화됨
    }
}

view 가 아니라 data 를 반환한다. @Controller 와 가장 큰 차이.

@Configuration

@Configuration
public class DataConfig {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(...);
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());   // ← 직접 호출
    }
}

@Configuration 클래스는 CGLIB proxy 로 감싸진다. 그래서 dataSource() 직접 호출이 매번 새 인스턴스를 만들지 않고 이미 등록된 singleton Bean 을 반환.

@Configuration 없이 @Component 로 같은 코드를 쓰면 (또는 proxyBeanMethods = false):

@Configuration(proxyBeanMethods = false)   // proxy 비활성
public class DataConfig {
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());   // ❌ 새 DataSource 인스턴스 생성됨
    }
}

대신 method 의 파라미터로 주입받아야:

@Configuration(proxyBeanMethods = false)
public class DataConfig {
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

Spring Boot 권장은 proxyBeanMethods = false (proxy 오버헤드 제거, native image 친화적). 단, @Bean 끼리 직접 호출은 못 하므로 파라미터 주입 패턴 강제됨.

메타 어노테이션 (커스텀 stereotype)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional(readOnly = true)
public @interface ReadOnlyService {}

@ReadOnlyService   // = @Service + @Transactional(readOnly=true)
public class ProductQueryService { ... }

반복되는 어노테이션 조합을 한 번에. Spring 자체 어노테이션 중에도 메타 어노테이션 합성이 흔함 (@RestController, @SpringBootApplication, @RequestScope).

스캔 필터링

@ComponentScan(
    basePackages = "com.example",
    includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyMarker.class),
    excludeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Test")
)
public class AppConfig {}

테스트 클래스 제외, 특정 어노테이션만 포함 등.

useDefaultFilters = false 로 stereotype 자동 매칭 끄고 명시적 필터만 사용도 가능.

AOP 와의 조합

// 모든 @Service 메서드에 advice
@Aspect
@Component
public class ServiceAuditAspect {
    @Around("within(@org.springframework.stereotype.Service *)")
    public Object audit(ProceedingJoinPoint pjp) throws Throwable {
        // ...
        return pjp.proceed();
    }
}

within(@Service *) 처럼 stereotype 별로 advice 적용 가능. layer 별 횡단 관심사에 유용.

함정과 베스트 프랙티스

  • 의미에 맞는 stereotype 선택: @Service@Repository 를 섞으면 layer 가 불분명
  • @Repository 가 아닌 곳에서 SQLException 받으면 변환 안 됨: DAO 는 반드시 @Repository
  • @ConfigurationproxyBeanMethods = false 는 native image 환경에서 필수
  • 컴포넌트 스캔 범위 좁히기: large 프로젝트에서 시작 시간 단축
  • stereotype 위에 메타 어노테이션 합성 으로 반복 줄이기
  • @Controller vs @RestController: view 반환은 @Controller, JSON 반환은 @RestController

관련 위키

이 글의 용어 (7개)
[Spring] @Autowired 해결 전략: @Qualifier, @Primary, byTypespring
정의 가 의 기초 (생성자/필드/setter 주입) 를 다룬다면, 이 페이지는 같은 타입의 Bean 이 여러 개일 때 어떻게 하나를 골라내는지 의 해결 전략을 다룬다. 기본 알고…
[Spring] AOP: @Aspect, Pointcut, Advicespring
정의 AOP (Aspect-Oriented Programming)는 cross-cutting concerns(로깅, 트랜잭션, 보안 등)를 비즈니스 코드에서 분리하는 기법. Sp…
[Spring] Bean Lifecycle: 초기화와 소멸 콜백spring
정의 가 Bean 등록과 주입 을 다룬다면, 이 페이지는 등록 이후 생성 → 사용 → 소멸 사이에 끼어드는 콜백을 다룬다. Spring 컨테이너가 단순히 객체를 만들고 끝내는 게…
[Spring] IoC와 DI: Bean, ApplicationContext, @Autowiredspring
정의 IoC (Inversion of Control)는 객체 생성/생명주기 제어를 프레임워크가 가져가는 원칙. DI (Dependency Injection)는 그 구현 방법. S…
[Spring] Spring Data JPA: Repository, Query Methodsspring
정의 Spring Data JPA는 JPA(Hibernate) 위에 Repository 추상화를 얹어 boilerplate를 제거. 인터페이스만 정의하면 Spring이 구현체 자…
[Spring] Spring MVC: @RestController, @RequestMappingspring
정의 Spring MVC는 Servlet 기반 동기 웹 프레임워크. 이 요청을 받아 핸들러(컨트롤러 메서드)에 라우팅. REST API와 전통 MVC(HTML) 모두 지원. We…
[Spring] Transaction: @Transactional, propagation, isolationspring
정의 Spring 트랜잭션은 AOP 기반 선언형 트랜잭션 관리. 한 줄로 메서드 전체를 트랜잭션 범위로. JDBC, JPA, MongoDB 등 backend에 동일 API. 기본…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기