🧩 Spring Bean 등록 어노테이션 정리: @Component부터 @Mapper까지
Spring을 사용하다 보면 자연스럽게 접하게 되는 어노테이션들이 있다. @Component, @Controller, @Service 등인데 각각의 어노테이션들의 공통점과 차이점에 대해 알아보자.
1) @Component
@Component는 Spring에서 가장 기본적인 컴포넌트 등록용 어노테이션이다. 컴포넌트란 애플리케이션을 구성하는 독립적이고 재사용 가능한 단위를 의미한다.
이 어노테이션이 붙은 클래스는 Spring의 컴포넌트 스캔 대상이 되어 자동으로 Bean으로 등록된다.
Spring Bean 이란?
Spring IoC Container가 생성하고 생명 주기까지 관리하는 객체를 의미한다. 개발자가 직접 new로 객체를 생성하지 않고 Spring이 대신 생성해 준 객체(Bean)을 주입받아 사용할 수 있다.
사실 나머지 어노테이션(@Controller, @Service 등등)은 모두 내부적으로는 @Component를 포함하고 있는 특수화 어노테이션이다. 즉 전부 @Component처럼 Spring이 자동으로 Bean으로 등록하여 생명 주기를 관리한다.
아래는 @Service 어노테이션의 실제 코드이다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
/**
* Alias for {@link Component#value}.
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
하지만 굳이 어노테이션을 구분해서 사용하는 이유는 애플리케이션의 계층 구조를 명확히 구분하고 각 클래스의 역할을 분명하게 표현하기 위함이다.
계층에 따라 어노테이션을 다르게 적용함으로써 코드의 가독성이 향상되고 역할에 따른 관심사를 분리할 수 있다. 이러한 구조는 AOP를 적용할 때나 테스트 코드를 작성할 때도 유리하게 작용한다.
2) @Controller & @RestController
@Controller는 전통적인 Spring MVC 패턴에서 웹 요청을 처리하고 View(JSP, Thymeleaf 등)를 반환하는 데 사용되는 어노테이션이다.
이 어노테이션이 붙은 클래스는 클라이언트의 요청을 받아 비즈니스 로직을 호출하고 그 결과를 Model에 담아 View로 전달하는 역할을 한다.
@Controller
public class MainController {
@GetMapping("/")
public String home(Model model) {
model.addAttribute("msg", "Hello World");
return "home"; // home.html 혹은 home.jsp View 렌더링
}
}
반면 @RestController는 @Controller와 유사하지만 내부적으로 @ResponseBody가 결합되어 있어 응답 결과를 View가 아닌 JSON이나 XML과 같은 데이터 형태로 반환한다.
@RestController
public class ApiController {
@GetMapping("/api/hello")
public String hello() {
return "Hello I'm BlueCool"; // 문자열이 그대로 HTTP 응답 본문으로 전송됨
}
}
따라서 @Controller는 일반적으로 템플릿 기반의 웹 애플리케이션에서 사용되고 @RestController는 RESTful API 서버를 개발할 때 클라이언트(React, Vue 등)와 데이터를 주고받을 때 사용된다.
3) @Service
@Service는 비즈니스 로직을 담당하는 클래스에 사용되는 어노테이션이다. 예를 들어 컨트롤러에서 요청을 받아 처리해야 하는 로직(회원 가입, 알림 발송 등)이 있다면 그 구체적인 로직은 @Service 클래스에서 구현한다.
@Service
public class MemberService {
public void registerMember(Member member) {
// 회원가입 처리 로직
}
}
@Service는 특별한 기능을 제공하진 않지만 비즈니스 계층(Service Layer)임을 명확히 표현해 주기 때문에 코드의 구조를 이해하고 유지 보수하기 쉽게 만들어준다.
또한 AOP(Pointcut)를 적용할 때 @Service가 붙은 클래스만을 대상으로 지정할 수 있기 때문에 로깅, 트랜잭션 관리 등에서 유용하게 활용된다.
4) @Repository & Mapper
@Repository는 데이터베이스 연동을 담당하는 DAO(Data Access Object) 클래스에 사용하는 어노테이션이다. @Repository는 다른 어노테이션들과는 다르게 Spring이 자동으로 데이터 접근 예외를 변환해 주는 기능이 추가되어 있다.
즉 SQLException과 같은 예외가 발생했을 때 Spring은 이를 DataAccessException으로 감싸서 던져준다. 이를 통해 DB 벤더에 독립적인 예외 처리 코드를 작성할 수 있다.
@Repository
public class MemberRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public Member findById(Long id) {
String sql = "SELECT * FROM members WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Member.class), id);
}
}
MyBatis를 사용하는 경우에는 DAO 클래스를 따로 만들지 않고 인터페이스에 @Mapper 어노테이션을 붙여 SQL과 매핑하는 방식을 사용한다.
즉 @Mapper가 붙은 인터페이스 역시 DAO 역할을 수행하는 컴포넌트이며 Spring이 자동으로 구현체를 만들어 Bean으로 등록한다.
@Mapper
public interface MemberMapper {
@Select("SELECT * FROM members WHERE id = #{id}")
Member findById(@Param("id") Long id);
}
@Mapper 역시 내부적으로 Spring의 예외 변환 기능이 적용되어 있어 데이터 접근 중 발생하는 SQLException 등의 예외를 DataAccessException 계열로 변환해 준다.
Spring에서 어노테이션을 구분해서 사용하는 이유는 단순히 Bean을 등록하기 위함도 있지만 계층별 책임을 명확히 나누고 코드 구조를 이해하기 쉽게 하기 위함이다.
따라서 각 어노테이션을 계층 구조와 책임 분리라는 관점에서 이해하고 사용하는 것이 Spring 애플리케이션을 더 유지 보수하기 쉬운 구조로 만들어 준다.