본문 바로가기
👨‍💻 프로그래밍/Java, Kotlin, Spring

Spring의 @Controller, @Repository, @Service, @Component 등 알아보기

by 개발자 진개미 2024. 5. 1.
반응형

스테리오타입 어노테이션 (Sterotype Annotation) 이란?

  • Spring에서는 @로 시작하는 Annotation을 굉장히 많이 사용합니다.
  • Annotation을 많이 사용하기 때문에 여러개를 동시에 쓰는 상황이 반복되거나, 기존 어노테이션의 동작을 약간 수정하면서도 비슷한 역할을 하는 새로운 어노테이션을 만들어야 하는 상황이 발생할 수 있습니다.
  • 이 경우에 여러개의 Annotation을 합쳐 스테리오타입 어노테이션 (Sterotype Annotation)을 만들 수 있습니다.
  • 스테리오타입 어노테이션 (Sterotype Annotation)을 사용하면 여러 개의 어노테이션을 적용해야 하는 상황을 단순화 하기 때문에 코드의 유지보수성을 높일 수 있습니다.

 


Spring Bean을 만드는 스테리오타입 어노테이션 (Sterotype Annotation)

  • Spring BeanSpring Container에 의해 관리되는 객체를 말합니다.
  • 쉽게 말하면 객체를 직접 만들고 의존성을 주입하는게 아니라 스프링이 이걸 대신 해 주면 그 객체는 Bean이라고 합니다.
  • Spring에 Bean을 등록하기 위해서는 당연히 Spring에 알려줘야 하는데요. 옛날에는 XML을 사용하기도 했지만 요즘은 거의 대부분 Annotation을 사용합니다.
  • 하지만... 헷갈리게도 Spring Bean을 등록하기 위한 Annotation은 1개가 아닙니다. 물론 기본적으로 Spring에 Bean으로 등록한다는 목적은 같지만, 이 외에도 추가적인 동작을 하거나 의미적으로 다른 경우가 있습니다.

 

예를 들어 @Service를 살펴보겠습니다. 모든 Spring Bean의 스테리오타입 어노테이션은 (Sterotype Annotation)은 @Component가 바탕입니다. 실제로 @Service를 살펴보면 @Component가 붙어 있는데요.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 까꿍
public @interface Service {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

하지만 @Service는 일반적인 Bean과 달리 비즈니스 로직을 처리하는 객체에 붙이는게 대부분입니다. 어쩌구Service, 어쩌구UseCase 등에 붙입니다.

@Service의 경우 의미가 다를 뿐이지 대신 @Component를 붙인다고 달라지는게 1도 없습니다. 하지만 @Repository, @Controller, @Configuration은 의미적인 차이 외에도 추가적인 동작을 하는데요. 이는 밑에서 자세히 알아보겠습니다.


요약

  의미 추가적인 동작
@Component - -
@Controller Web 요청을 처리함 Spring MVC에서 Controller로 인식함
@Service 비즈니스 로직을 처리함 -
@Repository 데이터 관련 동작을 처리함 DB의 예외를 Spring의 예외로 바꿔줌
@Configuration + @Bean 설정 관련 CGLib으로 Singleton을 보장

추가로 하는 것 - @Controller

  • HTTP 요청이 들어오면 Spring MVC Container는 특정 함수를 호출해 요청을 처리합니다.
  • 이때 여러 편의성을 위해 Spring MVC Controller는 다양한 기능이 있습니다.
    • String을 return하면 ViewResolver가 자동으로 해당 이름의 View를 찾아 줌
    • 자바 객체를 return하면 JSON으로 변환하거나 View에서 사용할 수 있도록 전달해 줌
    • 함수의 Parameter는 자동으로 URL의 Param과 연결 시켜 줌
  • 이 모든 기능을 사용하기 위해서는 Spring MVC Controller가 되야 하는데... @Controller만 붙이면 바로 Spring Web MVC Controller가 될 수 있습니다!

추가로 하는 것 - @Repository

  • 데이터를 접근하다 보면 다양한 문제가 생깁니다.
  • 네트워크가 이상할 수도 있고, Lock 관련 문제일 수도 있고, Transaction 관련 문제일 수도 있습니다.
  • 순수 자바 (JDBC 등)에서 데이터 접근 과정에 문제가 생기면 던지는 예외에는 2가지 문제점이 있습니다.
    • 1 - 이런 예외는 보통 복구 불능인데 (할 수 있는게 없는데) Checked Exception
    • 2 - 일반적인 예외를 던지고 (SQLException 등) 그 안에 에러코드가 들어 있음
  • @Repository를 붙이면 이 문제를 해결해 줍니다.

 

🐜 1 - 이런 예외는 보통 복구 불능인데 (할 수 있는게 없는데) Checked Exception임

  • Java에서는 많은 예외가 try - catch로 반드시 처리해야 하는 Checked Exception입니다.
  • 일부의 경우 Checked Exception도 말이 됩니다.
  • 예를들어 평균을 계산해야 하는데 유저가 0을 넣어서 나누기 0 하는 중에 Arithmetic Exception이 발생하면 전체 요청이 실패하는게 아니라 이 예외를 처리해서 0을 return 하는게 더 좋을 수도 있습니다. 그리고 이걸 까먹지 않게 반드시 처리하도록 언어 차원에서 강제하면 좋을 수도 있습니다.
try {
	return sum / count;
} catch (ArithemtaticException e) {
	return 0
}

 

  • 하지만 DB에서 나는 예외를 생각해 보세요.
  • Timeout이 나거나, COL이 없거나, TABLE이 없거나, Lock이 안 잡히거나, Transaction이 Rollback 되거나 기타 DB에 다른 문제가 생겼는데 여기서 뭘 할 수 있을까요?
  • DB의 장애는 절대 일어나면 안 되고, 장애가 난 경우 가짜 데이터를 내릴 수도 없으니 이 예외를 처리할 필요가 없습니다
  • 처리를 해야 한다고 해도 전역적인 @ControllerAdvice로 처리할 수 있는데 모든 DAO에서 처리를 강제하는건 실용적이지 않습니다.

 

🐜 2 - 일반적인 예외를 던지고 (SQLException 등) 그 안에 에러코드가 들어 있음

  • Unique Constraint를 어겨서 예외가 발생했습니다.
  • 근데 그 예외가 SQLException입니다. 물론 안에 코드랑 설명은 있지만... 이 예외를 처리하려면 예외 코드나 설명에 대한 지식이 있어야 합니다.
  • 만약 여기서 DB를 바꾼다면? 예외 코드나 설명이 또 달라집니다.
  • 그래서 Spring에서는 DataAccessException을 최상위로한 DAO 관련 여러 예외를 추상화시켰습니다.
  • 이제 Unique Constraint를 어기면 SQLException이 아닌 DataIntegrityViolationException이 나옵니다.
  • DataIntegrityViolationException은 당연히 DataAccessException의 자식 관계입니다.

 

그래서 @Repository가 붙은 경우 Spring에서 SQLException 같은 일반적인 예외를 쉽게 알 수 있는 잘 추상화된 Spring의 예외로 변환해 주고, Unchecked Exception으로 바꿔서 선택적으로 처리할 수 있게 해 줍니다.


추가로 하는 것 - @Configuration

@Configuration은 내부의 @Bean이 붙은 함수를 호출해서 Spring Bean을 얻는 역할을 합니다.

@Configuration
public ExampleConfig {
	@Bean
    public Example example() {
    	return Example();
    }
}

 

하지만 이상하지 않으신가요? 

  • Spring이 Bean을 얻고 싶을 때 마다 @Bean이 붙은 함수를 호출
  • 하지만 Spring의 Bean은 Singleton Scope인 경우가 있음
  • Spring이라고 자바 코드를 마음대로 바꿀 수 있는게 아닌데 매번 호출해야 한다면 Single Scope를 어떻게 보장하지?

정답은 @Configuration에 있습니다. @Configuration이 붙으면 Spring에서 CGLib이라는 바이트 코드를 조작할 수 있는 라이브러리를 사용해서 원래 Class를 상속한 Proxy Class를 대신 Spring Bean으로 등록합니다.

위의 예시의 경우 아래와 같은 형식이 (정확하진 않겠지만) 생깁니다.

public class ExampleConfig$$EnhancedBySpringCGLIB extends ExampleConfig {

    private Example example; // 1번 만들면 재사용하기 위해 요기에 저장

    @Override
    public Example example() {
        if (this.example == null) {
            this.example = super.example(); // 1번도 만들지 않은 경우 만들고 저장
        }
        return this.example; // 저장된 인스턴스를 재사용
    }
}

반응형