[Spring] @Component, @Controller, @Service, @Repository에 대해 알고 계시나요?

예름·2025년 4월 11일
5

Spring

목록 보기
1/3

📍 Bean이란?
📍 @Component, @Controller, @Service, @Repository의 차이

🔎 Bean이란?

@Component, @Controller, @Service, @Repository 들을 알기 위해서는 먼저 Bean에 대해 알아야 합니다.

Spring에서는 객체를 직접 new로 만들지 않고, 대신 Spring 컨테이너가 객체를 만들어서 관리합니다. 이렇게 Spring이 관리하는 객체를 Bean(빈) 이라고 합니다.

순수 Java vs Spring

우리가 스프링을 사용하지 않으면 아래와 같이 new를 사용해서 객체를 생성합니다.

public class MyService {
	...
}

public static void main(String[] args) {
	MyService service = new MyService();
}

하지만 스프링에서는

@Service
public class MyService {
	// 이 클래스는 Spring이 직접 만들어서 관리해주는 객체가 됩니다.
}

위처럼 @Service와 같은 애노테이션을 붙이면 Spring이 "이 클래스를 내가 관리해야겠다!" 하고 빈으로 등록해줍니다. Spring은 서비스, 리포지토리, 컨트롤러 같은 객체들을 대신 만들어서 메모리에 올려주고,
필요한 곳에 자동으로 넣어줍니다. 이것을 의존성 주입이라고 합니다.

Bean에 대해서는 다른 글에서 자세하게 정리하도록 하겠습니다.


🔎 @Component, @Service, @Controller, @Repository 차이

위 네 개의 애노테이션은 모두 Spring Bean으로 등록하라는 표시로, 역할에 따라 구분되어 있습니다.

💡 @Component

특정한 역할(Controller, Service, Repository)에 딱 맞지 않는 공통 기능이나 유틸리티 컴포넌트를 만들 때 사용합니다.

  • 일반적인 클래스를 빈으로 등록하고 싶을 때 사용
  • 다른 클래스에서 @Autowired나 생성자 주입으로 사용 가능

예시

@Component
public class PasswordEncoder {
	public void send(String message) {
        System.out.println("알림 발송: " + message);
    }
}

💡 @Controller

HTTP 요청을 받고, 응답을 리턴하는 클래스에서 사용합니다.

  • Spring MVC가 @Controller를 통해 이 클래스가 요청 핸들러라는 걸 인식하고 URL을 매핑해줌
  • @Component만 붙이면 요청을 처리하지 않음 (Spring 6 이후부터 더 엄격해짐)
@Controller
public class UserController {
    private final UserService userService;

	// 생성자는 @AllArgsConstructor, @RequiredArgsConstructor, @NoArgsConstructor로 대체할 수 있습니다.
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/users")
    public String signup(@RequestParam String name) {
        userService.signup(name);
        return "success";
    }
}

REST API라면 @RestController를 사용해서 JSON 리턴도 가능합니다.

📌 @RestController@Controller@ResponseBody가 결합된 형태로,
메서드가 JSON이나 문자열을 그대로 응답 본문에 담아 리턴합니다.

@RestController
public class ApiController {
    @GetMapping("/api/hello")
    public String hello() {
        return "Hello, world!";
    }
}

💡 @Service

비즈니스 로직을 처리하는 클래스에서 사용합니다. ex) 포인트 계산, 회원 등급 처리 등

  • "이 클래스는 비즈니스 로직을 처리하는 서비스야" 라고 Spring에게 알려줌
  • 유지보수나 AOP에서 역할 기반 포인트컷을 적용할 수 있음 ex) 서비스 로직에만 트랜잭션 걸기

예시

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void signup(String name) {
        // 사용자 생성 비즈니스 로직
        userRepository.save(new User(name));
    }
}

💡 @Repository

DB랑 직접 통신하는 클래스에서 사용합니다.

  • DB 작업 중 예외가 발생하면 Spring이 이를 DataAccessException 계열로 감싸서 던져줌
  • 트랜잭션 적용이나 예외 처리에서 일관된 처리가 가능해짐

예시

@Repository
public class UserRepository {
    private final EntityManager em;

    public UserRepository(EntityManager em) {
        this.em = em;
    }

    public void save(User user) {
        em.persist(user);
    }
}

❓ 전부 @Component를 쓰면 안되나요?

이론상 가능하지만 결론부터 말하면 네 안됩니다!

💡 @Controller 대신 @Component를 쓰면?

Spring 6(Spring Boot 3) 이전 버전에서는 @Component + @RequestMapping으로도 Bean 및 핸들러로 등록되었습니다. 하지만 Spring 6 이후 부터 @Controller 외에는 핸들러로 등록하지 않아 @Component를 붙이면 웹 요청을 정상적으로 수행할 수 없습니다.

// 이렇게 하면 동작하지 않습니다. (Spring 6부터)
@Component
public class MyController {
    @GetMapping("/hello")
    public String hello() { return "hi"; }
}
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
		implements MatchableHandlerMapping, EmbeddedValueResolverAware {
    ...
    @Override
    protected boolean isHandler(Class<?> beanType) {
        return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class); // 컨트롤러 애너테이션인지 확인
    }
    ...
}

💡 @Repository 대신 @Component를 쓰면?

DB 관련 예외가 Spring의 통합 예외 처리(DataAccessException)로 변환되지 않습니다.
Spring은 @Repository를 보면 예외를 감싸서 알려주는데, 이게 없으면 DB 오류가 생겼을 때 일관되게 처리하지 못합니다.

💡 AOP에서의 문제

AOP는 포인트컷을 정의할 때 "@Service가 붙은 클래스에만 적용해" 이런 식으로 계층 구분을 사용합니다. 전부 @Component만 쓰면 이런 구분이 안 되어서 AOP도 제대로 적용하기 어렵습니다.

💡 정리

애노테이션역할@Component로 대체 시 문제점
@ControllerHTTP 요청 처리요청 핸들러로 인식되지 않음 (Spring 6부터)
@Service비즈니스 로직AOP 포인트컷 적용 어려움
@RepositoryDB 연동예외 변환 기능 사라짐
profile
안정적인 쳇바퀴를 돌리는 삶

4개의 댓글

comment-user-thumbnail
2025년 4월 11일

하나 배웠습니다

1개의 답글
comment-user-thumbnail
2025년 4월 11일

글이 너무 좋네요 구독 좋아요 하고 갑니다~^^

1개의 답글