@Repository
1. 개요
1. 개요
@Repository는 스프링 프레임워크에서 제공하는 스테레오타입 어노테이션으로, 데이터 접근 계층(DAO, Data Access Object)을 구성하는 컴포넌트를 표시하는 데 사용된다. 이 어노테이션은 주로 데이터베이스, 파일 시스템과 같은 데이터 저장소에 접근하는 로직을 캡슐화하는 빈(Bean)에 적용된다. 스프링 2.0 버전에서 처음 도입되었으며, 스프링 개발팀에 의해 개발되었다.
@Repository는 스프링 컨테이너가 해당 클래스를 자동으로 스캔하여 애플리케이션 컨텍스트에 빈으로 등록하도록 지시한다. 이는 영속성(Persistence)과 관련된 예외를 스프링의 통일된 데이터 접근 예외 계층구조로 변환해주는 특별한 처리 기능을 포함하고 있다. 따라서 개발자는 데이터 접근 기술(예: JDBC, JPA, Hibernate)에 따라 상이한 예외 처리를 일관된 방식으로 관리할 수 있게 된다.
이 어노테이션은 스프링 부트를 포함한 현대적인 스프링 기반 애플리케이션에서 데이터 레이어를 명확하게 구분하고 표준화하는 핵심 도구로 자리 잡았다. @Repository가 부여된 클래스는 비즈니스 로직 계층(서비스 계층)과 프레젠테이션 계층(컨트롤러 계층)으로부터 데이터 접근 책임을 분리하는 데 기여한다.
2. 역할과 목적
2. 역할과 목적
@Repository 어노테이션의 핵심 역할은 스프링 프레임워크 애플리케이션에서 데이터 접근 계층(DAO, Data Access Object)을 명시적으로 표시하는 것이다. 이는 계층화 아키텍처 패턴을 따르는 애플리케이션에서 비즈니스 로직을 담당하는 서비스 계층과 데이터 저장소를 직접 다루는 로직을 분리하는 데 기여한다. @Repository가 부여된 클래스는 일반적으로 데이터베이스나 파일 시스템과 같은 영속성(Persistence) 메커니즘에 접근하여 데이터의 생성, 조회, 갱신, 삭제(CRUD) 작업을 수행하는 책임을 가진다.
이 어노테이션의 주요 목적은 두 가지로 요약된다. 첫째, 스프링 컨테이너에게 해당 클래스가 데이터 접근 컴포넌트임을 알려 빈(Bean)으로 자동 등록되게 하는 것이다. 이는 컴포넌트 스캔 기능을 통해 이루어진다. 둘째, 그리고 더 중요한 목적은 플랫폼별 예외를 스프링의 통일된 데이터 접근 예외 계층구조로 변환하는 것이다. 예를 들어, JDBC가 던지는 SQLException이나 JPA의 PersistenceException과 같은 특정 기술에 종속된 예외를 스프링의 DataAccessException으로 변환함으로써, 서비스 계층이 데이터 접근 기술의 변경에 영향을 받지 않고 일관된 방식으로 예외를 처리할 수 있게 한다.
3. 주요 특징
3. 주요 특징
@Repository 어노테이션은 스프링 프레임워크의 핵심 스테레오타입 중 하나로, 데이터 접근 계층을 명시적으로 표시하는 데 사용된다. 이 어노테이션의 가장 중요한 특징은 스프링 컨테이너가 이 클래스를 빈으로 자동 등록하도록 한다는 점이다. 이는 컴포넌트 스캔 기능과 결합되어, 개발자가 수동으로 빈 설정을 하지 않아도 의존성 주입이 가능한 컴포넌트로 만들어 준다.
@Repository의 또 다른 주요 특징은 예외 변환 메커니즘을 제공한다는 것이다. 영속성 계층에서 발생하는 데이터베이스 특화 예외(예: JDBC의 SQLException이나 JPA의 PersistenceException)를 스프링의 통일된 DataAccessException 계층구조로 변환해 준다. 이를 통해 비즈니스 계층이나 표현 계층에서는 데이터 접근 기술에 독립적인 방식으로 예외를 처리할 수 있어, 느슨한 결합과 코드의 유지보수성을 높인다.
이 어노테이션은 DAO 패턴을 구현하는 클래스에 적용되는 것이 일반적이다. 데이터베이스나 파일 시스템과 같은 저장소에 대한 CRUD 연산을 캡슐화하는 로직을 담당하며, @Component 어노테이션의 특수한 형태로 간주된다. 따라서 @Repository가 부여된 클래스는 기본적인 빈 등록 및 의존성 주입의 모든 이점을 가지면서, 계층별 아키텍처에서의 역할을 명확히 부여받는 추가적 의미를 갖게 된다.
4. 사용 방법
4. 사용 방법
@Repository 어노테이션을 사용하는 기본적인 방법은 데이터 접근 객체(DAO) 역할을 하는 클래스에 해당 어노테이션을 선언하는 것이다. 이 클래스는 일반적으로 인터페이스를 구현하거나, JPA의 리포지토리 인터페이스를 상속받아 데이터베이스 CRUD 연산을 수행하는 메서드를 포함한다. 스프링 컨테이너는 애플리케이션 시작 시 @Repository가 붙은 클래스를 스캔하여 빈(Bean)으로 등록한다. 이를 통해 다른 서비스 계층이나 컨트롤러에서 의존성 주입(Dependency Injection)을 통해 해당 빈을 쉽게 주입받아 사용할 수 있다.
구체적인 사용법은 선택한 영속성(Persistence) 기술에 따라 다르다. 전통적인 JDBC 템플릿을 사용할 때는 JdbcTemplate을 주입받는 클래스에 @Repository를 적용한다. 하이버네이트(Hibernate)나 JPA를 사용하는 경우, EntityManager를 활용하거나 JpaRepository 인터페이스를 상속한 인터페이스 자체에 @Repository를 명시적으로 선언할 필요는 없다. 스프링 데이터 JPA가 제공하는 리포지토리 인터페이스를 상속하면 구현체가 런타임에 자동 생성되고, 이는 이미 @Repository의 의미를 내포하기 때문이다. 그러나 커스텀 구현 클래스를 별도로 작성한다면 그 클래스에 @Repository를 선언해야 한다.
@Repository는 @Component 어노테이션을 메타 어노테이션으로 포함하므로, 컴포넌트 스캔(Component Scan)의 대상이 된다. 따라서 @SpringBootApplication 어노테이션이 있는 메인 클래스나 @ComponentScan이 설정된 설정 클래스(Configuration Class)의 패키지 하위에 @Repository 클래스가 위치하면 자동으로 빈으로 등록된다. 수동으로 빈 정의(Bean Definition)를 자바 설정(Java Config)이나 XML 설정으로 작성할 때는 @Repository 대신 빈 정의 코드를 작성해야 한다.
사용 기술 | 적용 대상 |
| 비고 |
|---|---|---|---|
JDBC 템플릿 | 구체적인 DAO 구현 클래스 | 필요 | 클래스에 직접 선언 |
| 필요 | 클래스에 직접 선언 | |
| 불필요 | 인터페이스 상속만으로 충분 | |
커스텀 구현체 클래스 | 필요 | 구현 클래스에 직접 선언 |
5. 다른 스테레오타입과의 비교
5. 다른 스테레오타입과의 비교
5.1. @Repository vs @Component
5.1. @Repository vs @Component
@Repository와 @Component는 모두 스프링 프레임워크에서 빈으로 등록하기 위해 사용하는 어노테이션이다. 그러나 그 용도와 제공하는 추가 기능에서 명확한 차이가 있다. 가장 근본적인 차이는 @Component가 모든 스프링 컴포넌트를 표시하는 범용(general-purpose) 어노테이션인 반면, @Repository는 특정 아키텍처 계층, 즉 데이터 접근 계층(DAO)을 명시적으로 표시하는 스테레오타입 어노테이션이라는 점이다.
구체적으로, @Component는 클래스를 스프링의 관리 대상 빈으로 선언하는 기본적인 역할을 한다. @Service나 @Controller와 같은 다른 스테레오타입 어노테이션들도 내부적으로 @Component를 포함하고 있어, 결국 모두 컴포넌트 스캔의 대상이 된다. 반면 @Repository는 @Component의 기능을 모두 포함하면서, 데이터 접근 객체에 특화된 추가적인 의미와 기능을 부여한다. 이는 주로 예외 변환 메커니즘이다.
따라서 사용 방법에 있어 @Component는 도메인 서비스나 유틸리티 클래스와 같이 특정 계층으로 분류하기 어려운 일반적인 빈에 사용하는 것이 적합하다. 한편, 데이터베이스나 파일 시스템과 같은 영속성 저장소에 접근하는 로직을 담당하는 DAO 클래스에는 @Repository를 사용하는 것이 바람직하다. 이는 코드의 의도를 명확히 하고, 스프링이 제공하는 데이터 접근 예외를 스프링의 데이터 접근 예외 계층으로 자동 변환해주는 이점을 얻기 위함이다.
5.2. @Repository vs @Service
5.2. @Repository vs @Service
@Repository와 @Service는 모두 스프링 프레임워크의 핵심 스테레오타입 어노테이션으로, @Component를 메타 어노테이션으로 포함하고 있어 스프링 컨테이너에 의해 빈으로 등록된다는 공통점을 가진다. 그러나 이 두 어노테이션은 애플리케이션 내에서 담당하는 역할과 책임의 영역이 명확히 구분된다. @Repository는 주로 데이터 접근 계층(DAO)을 표시하는 데 사용되며, 데이터베이스나 파일 시스템과 같은 영속성 저장소에 접근하여 데이터를 생성, 조회, 수정, 삭제하는 로직을 캡슐화한다. 반면, @Service는 비즈니스 로직 계층을 표시하는 데 사용되며, 애플리케이션의 핵심 업무 규칙을 구현하고, 여러 리포지토리나 다른 서비스의 메서드를 조합하여 복잡한 비즈니스 작업을 수행한다.
이러한 역할 구분은 계층형 아키텍처의 원칙을 따르며, 코드의 가독성, 유지보수성, 테스트 용이성을 높이는 데 기여한다. 예를 들어, 사용자 정보를 업데이트하는 작업은 @Service가 담당하는 비즈니스 로직(예: 유효성 검사, 트랜잭션 관리, 다른 도메인 규칙 적용)과 @Repository가 담당하는 데이터 접근 로직(예: 실제 SQL 실행 또는 JPA를 통한 엔티티 저장)으로 분리되어 구현된다. 따라서 @Service 어노테이션이 부여된 클래스는 일반적으로 트랜잭션 경계를 설정하거나 다른 서비스를 호출하는 진입점 역할을 한다.
개발 과정에서 두 어노테이션을 혼용하여 사용할 수는 있지만, 이는 관례를 벗어나며 아키텍처의 명확성을 해칠 수 있다. 스프링은 @Repository 어노테이션에 예외 변환이라는 특별한 부가 기능을 제공하여, JDBC나 JPA 등 다양한 데이터 접근 기술에서 발생하는 플랫폼 특화된 예외를 스프링의 통일된 DataAccessException 계층구조로 변환해준다. @Service 어노테이션은 이러한 예외 변환 기능을 제공하지 않으며, 주로 비즈니스 메서드에 트랜잭션 관리를 위한 @Transactional 어노테이션과 함께 사용된다. 결국, @Repository는 "데이터를 어떻게 다루는가"에, @Service는 "비즈니스 규칙에 따라 무엇을 하는가"에 초점을 맞춘다.
6. 예외 변환
6. 예외 변환
@Repository 어노테이션의 핵심 기능 중 하나는 데이터 접근 계층에서 발생하는 플랫폼별 예외를 스프링의 통일된 데이터 접근 예외 계층구조로 변환하는 것이다. 이는 영속성 기술(예: JDBC, JPA, Hibernate)마다 던지는 예외 클래스가 다르기 때문에 필요한 처리이다.
구체적으로, @Repository 어노테이션이 적용된 클래스는 스프링의 AOP (관점 지향 프로그래밍)를 통해 자동으로 프록시로 감싸진다. 이 프록시는 @Repository에 내재된 @Component 어노테이션을 통해 컴포넌트 스캔 대상이 되며, 동시에 PersistenceExceptionTranslationPostProcessor 빈에 의해 예외 변환 어드바이스가 적용된다. 이 어드바이스는 DataAccessException의 서브클래스들로 예외를 변환하는 역할을 수행한다.
예를 들어, JDBC를 사용할 때 발생하는 SQLException이나 JPA의 PersistenceException과 같은 특정 기술에 종속된 예외는, @Repository 계층을 통해 DataAccessException(예: DataIntegrityViolationException, EmptyResultDataAccessException)으로 변환되어 서비스 계층에 전달된다. 이를 통해 서비스 계층은 구체적인 데이터 접근 기술에 의존하지 않고 일관된 방식으로 예외를 처리할 수 있게 되어 느슨한 결합을 달성한다.
이러한 예외 변환 메커니즘은 개발자가 비즈니스 로직에 집중하고, 데이터 접근 기술을 변경하더라도 예외 처리 코드를 크게 수정하지 않아도 되게 해준다. 따라서 @Repository는 단순히 DAO를 표시하는 역할을 넘어, 스프링의 포괄적인 예외 처리 전략의 핵심 요소로 작동한다.
7. 구현 예시
7. 구현 예시
@Repository 어노테이션을 사용하는 구현 예시는 주로 데이터 접근 객체를 정의할 때 나타난다. 일반적으로 인터페이스를 생성하고, 이를 구현하는 클래스에 @Repository를 적용하거나, 스프링 데이터 JPA를 사용할 때 리포지토리 인터페이스 자체에 선언하는 방식으로 사용된다.
구체적인 예시로, 사용자 정보를 데이터베이스에서 조회하는 DAO를 구현한다고 가정해 보자. 먼저 UserRepository라는 인터페이스를 정의하고, 이를 구현하는 JpaUserRepository 클래스를 생성한다. 이 클래스에는 @Repository 어노테이션을 명시적으로 추가하여, 이 클래스가 데이터 접근 계층의 컴포넌트임을 스프링 컨테이너에 알린다. 클래스 내부에서는 JPA의 EntityManager나 스프링 데이터 JPA가 제공하는 JpaRepository 인터페이스를 상속받아 findById, save 같은 데이터 접근 메서드를 구현하게 된다.
또 다른 일반적인 예시는 스프링 데이터 JPA를 직접 활용하는 경우이다. MemberRepository 같은 인터페이스를 만들고, 여기에 @Repository를 붙이거나 (생략 가능한 경우가 많음) JpaRepository<Member, Long> 인터페이스를 상속받기만 해도 스프링이 런타임에 해당 인터페이스의 구현체를 자동으로 생성하여 빈으로 등록한다. 이때 @Repository의 역할은 해당 빈이 DAO이며, 영속성 예외를 스프링의 통합 데이터 접근 예외로 변환하는 변환기(PersistenceExceptionTranslationPostProcessor)의 적용 대상이 되도록 표시하는 것이다.
실제 코드에서는 @Repository가 붙은 클래스나 인터페이스는 다른 비즈니스 로직 계층의 컴포넌트, 예를 들어 @Service가 붙은 서비스 클래스에서 의존성 주입(@Autowired)을 받아 사용된다. 이를 통해 서비스 계층은 구체적인 데이터베이스 접근 기술(JDBC, JPA, 마이바티스 등)로부터 독립적으로 로직을 작성할 수 있게 된다.
