Topic (오늘의 주제)
스프링 스테레오타입 어노테이션 (@Component, @Service, @Repository, @Controller)
: 스프링 컨테이너에게 "이 클래스는 내가 관리할 빈(Bean)이야"라고 알려주는 표시이자, 해당 클래스의 **역할(Role)**을 명시하는 어노테이션들.
Why (왜 사용하는가? 왜 중요한가?)
- 실무: 모든 클래스에 @Component만 붙여도 동작은 하지만, @Service, @Controller 등으로 구분해야 "이 코드가 무슨 일을 하는지" 명확히 알 수 있어 가독성과 유지보수성이 좋아진다.
- 구조적 의미: 스프링의 **계층형 아키텍처(Layered Architecture)**를 구현하는 핵심 도구다. 웹 계층, 비즈니스 계층, 데이터 계층을 분리한다.
- 면접 의도: "그냥 다 @Component 쓰면 안 되나요?"라는 질문에 대해, 각 어노테이션의 의미적 차이와 **기술적 차이(특히 Repository)**를 설명할 수 있는지 확인한다.
Core Concept (핵심 개념 정리)
사실 @Controller, @Service, @Repository는 내부를 뜯어보면 모두 @Component를 포함하고 있습니다. 즉, "특수한 목적을 가진 @Component"입니다.
| 어노테이션 | 계층 (Layer) | 핵심 역할 및 차이점 | 기술적 특징 |
| @Component | 공통 / 기타 | 가장 기본이 되는 어노테이션. | 특별한 추가 기능 없음. 유틸리티 클래스 등에 사용. |
| @Controller | 프레젠테이션 (Web) |
사용자의 요청(HTTP)을 받고 응답을 반환하는 역할. | 요청 매핑(@RequestMapping) 기능을 수행할 수 있음. |
| @Service | 비즈니스 (Business) |
핵심 업무 로직을 수행하는 역할. (트랜잭션 처리의 주 무대) |
기술적으로는 @Component와 똑같음. 하지만 "여기에 비즈니스 로직이 있다"는 명확한 **의미(Semantics)**를 부여함. |
| @Repository | 퍼시스턴스 (Data Access) |
DB에 접근하여 데이터를 저장/조회하는 역할. | DB 예외 변환 기능: DB마다 다른 지저분한 SQL 예외를 스프링의 깔끔한 DataAccessException으로 변환해 줌. |
💡 핵심: 왜 @Repository만 기능이 다를까?
- @Service는 개발자가 보기에 "아 서비스구나" 하고 알기 위한 용도가 큽니다.
- 하지만 @Repository는 실제로 AOP가 적용되어, DB에서 발생하는 체크 예외(SQLExcepiton 등)를 스프링이 정의한 언체크 예외(RuntimeException)로 자동 변환해 주는 중요한 기능을 수행합니다. 덕분에 서비스 계층이 특정 DB 기술에 종속되지 않게 해줍니다.
Interview Answer Version (면접 답변식 요약)
"이 어노테이션들은 모두 클래스를 스프링 빈으로 등록한다는 점은 같지만, 역할과 계층을 명시한다는 차이가 있습니다. @Controller는 웹 요청 처리를, @Service는 비즈니스 로직을, @Repository는 데이터 접근을 담당한다는 의미를 가집니다. 특히 **@Repository**는 단순히 빈 등록뿐만 아니라, DB에서 발생하는 구체적인 예외를 스프링의 추상화된 예외(DataAccessException)로 변환해 주는 예외 변환(Exception Translation) 기능을 추가로 제공한다는 기술적 차이가 있습니다."
Practical Tip (사용시 주의할 점 or 활용 예)
1. @RestController = @Controller + @ResponseBody
- 실무에서 API 서버(JSON 응답)를 만들 때는 @Controller 대신 **@RestController**를 주로 사용합니다.
- @Controller는 주로 HTML(View)을 반환하려 하지만, @RestController는 리턴값 자체를 데이터(JSON)로 보내기 때문입니다.
2. 계층을 엄격히 지키자
- @Controller에서 @Repository를 바로 호출하지 마세요.
- 간단한 조회라도 Controller → Service → Repository 흐름을 타야 합니다. 그래야 나중에 트랜잭션을 걸거나 로직을 확장할 때 구조가 무너지지 않습니다.
3. 아무거나 스캔하지 말 것
- Entity(DB 테이블 객체)나 DTO(데이터 전송 객체)에는 절대 이 어노테이션들을 붙이면 안 됩니다. 이들은 빈으로 관리되는 객체가 아니라, 빈들이 데이터를 주고받을 때 사용하는 '데이터' 그 자체이기 때문입니다.
예상 꼬리 질문 정리
- "그럼 @Service 어노테이션은 기능적으로 @Component와 100% 똑같나요?"
- (현재 버전까지는 기능적으로 동일함. 하지만 AOP Pointcut의 대상이 되거나, 개발자 간의 약속(관례)으로서의 가치가 매우 크다는 점을 강조)
- "스프링 부트에서는 컴포넌트 스캔(Component Scan)이 어떻게 자동으로 일어나나요?"
- (@SpringBootApplication 안에 @ComponentScan이 들어있어서, 해당 클래스 위치부터 하위 패키지를 다 뒤진다는 원리 설명)
- "@Controller와 @RestController의 차이는 무엇인가요?"
- (@ResponseBody의 포함 여부와, View Resolver를 거치냐 Message Converter를 거치냐의 차이 설명)
Deep Dive (공부하며 몰랐던 개념)
1. @Controller와 @RestController의 차이
면접에서 가장 많이 나오는 질문 중 하나이자, 실무에서 혼동하기 쉬운 개념입니다. 핵심은 "반환하는 것이 화면(View)인가, 데이터(Data)인가?"입니다.
- 공식: @RestController = @Controller + @ResponseBody
| 구분 | @Controller (전통적 웹) | @RestController (최신 API) |
| 주 목적 | **화면(View)**을 띄우기 위함 | **데이터(Data)**를 내려주기 위함 |
| 리턴값 | HTML 파일의 이름 (예: "home") | 데이터 객체 그 자체 (JSON 등) |
| 동작 원리 | View Resolver가 작동하여 HTML 파일을 찾음 | HttpMessageConverter가 작동하여 객체를 JSON으로 변환함 |
| 비유 | 식당에서 완성된 요리를 서빙함 | 식당에서 **밀키트(재료)**만 포장해줌 (요리는 클라이언트가 알아서 함) |
💡 참고: @Controller에서도 데이터를 보내고 싶다면 메서드 위에 @ResponseBody를 붙이면 됩니다. @RestController는 모든 메서드에 이것이 기본으로 붙어있는 셈입니다.
2. 그렇다면 객체는 어떻게 JSON으로 변하는가? (Jackson과 Getter)
@RestController가 객체를 반환할 때, 스프링 부트는 내장된 Jackson 라이브러리를 통해 자바 객체를 JSON 문자열로 변환(Serialization)합니다. 이때 가장 중요한 규칙은 **"Jackson은 Getter 메서드를 보고 데이터를 가져온다"**는 점입니다.
변환 과정 예시:
- 개발자: User 객체를 리턴 (필드: name, age / 메서드: getName(), getAge())
- Jackson: getName() 메서드 발견! $\rightarrow$ get을 떼고 소문자로 바꿔 **"name"이라는 Key를 만듦.
- Jackson: getName()을 실행해 반환된 값 "홍길동"을 Value로 넣음.
- 결과: {"name": "홍길동", "age": 20} 형태의 JSON 완성.
⚠️ 주의할 점 (실무 실수 1위):
DTO나 Entity 클래스에 Getter 메서드가 없으면 Jackson이 데이터를 꺼낼 수 없어 **빈 괄호 {}만 전송되거나 에러(406)**가 발생합니다. (Lombok의 @Getter가 필수인 이유입니다.)
'Archive > Daily Dev Q&A' 카테고리의 다른 글
| Daily Dev Q&A: @SpringBootApplication은 필수인가? (1) | 2025.12.30 |
|---|---|
| Daily Dev Q&A: Spring의 예외 처리 방식 (0) | 2025.12.29 |
| Daily Dev Q&A: Spring Bean (0) | 2025.12.26 |
| Daily Dev Q&A: Spring Boot (0) | 2025.12.26 |
| Daily Dev Q&A: IoC & DI (0) | 2025.12.26 |