스프링부트 애노테이션을 제대로 이해하지 못하면, 코드는 돌아가는데 왜 돌아가는지 모르는 상태가 됩니다. 매일 쓰는 @RestController, @Autowired, @Transactional… 문서 없이 그냥 복붙하고 계시진 않으신가요? 스프링부트는 애노테이션 기반 프로그래밍이 핵심이기 때문에, 각 애노테이션이 무엇을 해주는지, 언제 써야 하는지, 어떤 실수를 하면 안 되는지를 제대로 알아야 실무에서 진짜 실력을 발휘할 수 있습니다. 이 글에서는 실무에서 가장 빈번하게 등장하는 애노테이션 10가지를 원리부터 예제 코드·주의사항까지 한 번에 정리합니다.
목차
- 스프링부트 애노테이션이란? 기초 개념과 동작 원리
- 애플리케이션 구동의 출발점 – @SpringBootApplication
- 웹 레이어의 핵심 – @RestController와 @RequestMapping 계열
- 스프링의 심장 – @Autowired와 의존성 주입 3가지 방식
- 데이터와 트랜잭션 – @Entity, @Repository, @Transactional
- 설정과 예외 처리 – @Value, @Configuration, @ExceptionHandler
1. 스프링부트 애노테이션이란? 기초 개념과 동작 원리
애노테이션의 정체 – 코드에 달린 메타데이터
애노테이션(Annotation)은 @ 기호로 시작하는 메타데이터 마커입니다. Java 코드에 특별한 의미를 부여하는 일종의 표시인데, 스프링부트는 이 표시를 읽어서 자동으로 필요한 동작을 수행합니다.
일상적인 비유로 설명하면, 식당의 메뉴판에 붙어있는 스티커와 같습니다. ⭐(인기 메뉴), 🌶(매운맛), 🥗(채식)처럼, 개발자가 클래스나 메서드에 @Controller, @Service, @Transactional 같은 스티커를 붙이면 스프링이 그 스티커를 보고 “아, 이 클래스는 웹 요청을 처리하는 컨트롤러구나”, **”이 메서드는 트랜잭션 안에서 실행해야 하는구나”**라고 알아서 처리합니다.
java
// 애노테이션이 없는 순수 Java 클래스
public class UserService {
public void save(User user) { ... }
}
// 애노테이션을 붙인 스프링 관리 클래스
@Service // "스프링아, 이 클래스를 Bean으로 관리해줘"
@Transactional // "이 클래스의 메서드들은 트랜잭션 안에서 실행해줘"
public class UserService {
public void save(User user) { ... }
}
// → 스프링이 자동으로 Bean 등록 + 트랜잭션 처리
스프링부트가 애노테이션을 처리하는 방식
스프링부트는 애플리케이션 구동 시 **컴포넌트 스캔(Component Scan)**을 통해 지정된 패키지를 탐색하고, 스프링 관련 애노테이션이 붙은 클래스를 자동으로 발견해 **IoC 컨테이너(ApplicationContext)**에 Bean으로 등록합니다.
[스프링부트 애노테이션 처리 흐름]
애플리케이션 구동
↓
@SpringBootApplication 발견
↓
컴포넌트 스캔 시작 (해당 패키지 및 하위 패키지 전체 탐색)
↓
@Component, @Service, @Repository, @Controller 등 발견
↓
IoC 컨테이너에 Bean 등록
↓
@Autowired, @Value 등으로 의존성·설정값 주입
↓
애플리케이션 정상 구동 완료
이 전체 과정이 애노테이션 덕분에 XML 설정 파일 한 줄 없이 자동으로 이루어집니다. 이것이 스프링부트의 핵심 철학인 **”설정보다 관례(Convention over Configuration)”**입니다.
애노테이션의 3가지 분류
실무에서 만나는 스프링부트 애노테이션은 크게 세 가지 레이어로 분류할 수 있습니다.
| 분류 | 대표 애노테이션 | 역할 |
|---|---|---|
| 웹 레이어 | @RestController, @RequestMapping, @GetMapping 등 | HTTP 요청·응답 처리 |
| 서비스·Bean 레이어 | @Service, @Component, @Autowired, @Transactional | 비즈니스 로직·의존성 관리 |
| 데이터·설정 레이어 | @Entity, @Repository, @Value, @Configuration | DB 매핑·설정 관리 |
이 분류를 머릿속에 넣어두면, 낯선 애노테이션을 만났을 때도 “이건 어떤 레이어에서 쓰는 거지?”를 금방 파악할 수 있습니다.
2. 애플리케이션 구동의 출발점 – @SpringBootApplication
① @SpringBootApplication
모든 스프링부트 프로젝트의 main 클래스에 반드시 붙는 애노테이션입니다. 단 하나의 애노테이션이지만, 내부적으로 세 가지 핵심 애노테이션을 합친 복합 애노테이션입니다.
java
@SpringBootApplication // 이 한 줄이 아래 세 가지를 모두 포함
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication의 내부 구성:
java
// @SpringBootApplication은 사실 이 세 가지의 조합
@SpringBootConfiguration // = @Configuration: 이 클래스가 Bean 설정 클래스임을 선언
@EnableAutoConfiguration // 스프링부트 자동 설정 활성화 (classpath 기반 자동 Bean 등록)
@ComponentScan // 현재 패키지부터 하위 패키지까지 컴포넌트 자동 탐색
public @interface SpringBootApplication { ... }
| 포함 애노테이션 | 역할 |
|---|---|
@SpringBootConfiguration | 이 클래스를 스프링 설정 클래스로 등록 |
@EnableAutoConfiguration | 의존성 기반 자동 설정 활성화 (JPA 있으면 JPA 설정, Web 있으면 MVC 설정 등) |
@ComponentScan | 해당 패키지와 하위 패키지의 컴포넌트 자동 탐색 및 Bean 등록 |
실무 주의사항:
@SpringBootApplication이 붙은 클래스의 패키지 위치가 매우 중요합니다. @ComponentScan이 해당 패키지부터 탐색을 시작하기 때문에, main 클래스가 최상위 패키지에 있어야 모든 하위 패키지의 컴포넌트가 정상 등록됩니다.
✅ 올바른 패키지 구조:
com.example.myapp
├── MyApplication.java ← @SpringBootApplication 위치 (최상위)
├── controller/
│ └── UserController.java
├── service/
│ └── UserService.java
└── repository/
└── UserRepository.java
❌ 잘못된 구조 (controller/ 안에 main 클래스 위치):
com.example.myapp.controller
├── MyApplication.java ← service/, repository/ 탐색 불가!
3. 웹 레이어의 핵심 – @RestController와 @RequestMapping 계열
② @RestController
HTTP 요청을 받아 처리하는 컨트롤러 클래스에 붙이는 애노테이션입니다. @Controller와 @ResponseBody를 합친 복합 애노테이션으로, 반환값을 JSON(또는 XML)으로 자동 직렬화해 HTTP 응답 본문에 담아 보냅니다.
java
// @Controller vs @RestController 비교
@Controller
public class OldController {
@ResponseBody // 없으면 뷰(View) 이름으로 해석됨
@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll();
}
}
// ↓ @RestController 하나로 해결
@RestController // = @Controller + @ResponseBody
@RequestMapping("/api/v1") // 클래스 레벨 공통 URL 접두사
public class UserController {
private final UserService userService;
@GetMapping("/users") // GET /api/v1/users
public List<User> getUsers() {
return userService.findAll(); // List<User> → 자동으로 JSON 배열 변환
}
@GetMapping("/users/{id}") // GET /api/v1/users/1
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping("/users") // POST /api/v1/users
public ResponseEntity<User> createUser(@RequestBody UserDto dto) {
User saved = userService.save(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
}
③ @RequestMapping 계열 – URL 매핑 애노테이션
HTTP 요청의 URL과 메서드(GET, POST 등)를 특정 Java 메서드에 연결하는 애노테이션들입니다.
| 애노테이션 | HTTP 메서드 | 주요 용도 |
|---|---|---|
@GetMapping | GET | 데이터 조회 |
@PostMapping | POST | 데이터 생성 |
@PutMapping | PUT | 데이터 전체 수정 |
@PatchMapping | PATCH | 데이터 부분 수정 |
@DeleteMapping | DELETE | 데이터 삭제 |
@RequestMapping | 모든 메서드 | 공통 URL 접두사 또는 특정 메서드 지정 |
java
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
// @PathVariable – URL 경로에서 변수 추출
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) { ... }
// @RequestParam – 쿼리 파라미터 추출 (?page=1&size=10)
@GetMapping
public Page<Product> getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) { ... }
// @RequestBody – HTTP 요청 본문(JSON)을 Java 객체로 역직렬화
@PostMapping
public Product createProduct(@RequestBody @Valid ProductDto dto) { ... }
// @RequestHeader – HTTP 요청 헤더 값 추출
@DeleteMapping("/{id}")
public void deleteProduct(
@PathVariable Long id,
@RequestHeader("Authorization") String token) { ... }
}
실무 주의사항:
@RequestParam에서 required = true(기본값)인 파라미터가 요청에 없으면 400 Bad Request가 발생합니다. 선택적 파라미터는 반드시 required = false와 defaultValue를 지정하세요.
java
// ❌ 위험 – 파라미터 없으면 400 오류
@GetMapping("/search")
public List<Product> search(@RequestParam String keyword) { ... }
// ✅ 안전 – 파라미터 없어도 빈 결과 반환
@GetMapping("/search")
public List<Product> search(
@RequestParam(required = false, defaultValue = "") String keyword) { ... }
4. 스프링의 심장 – @Autowired와 의존성 주입 3가지 방식
④ @Autowired – 의존성 자동 주입
스프링 IoC 컨테이너에 등록된 Bean을 다른 Bean에 자동으로 주입하는 애노테이션입니다. 의존성 주입(Dependency Injection, DI)은 스프링의 핵심 철학이며, @Autowired는 그 구현 도구입니다.
의존성 주입 방식은 세 가지가 있으며, 실무에서 권장하는 방식이 다릅니다.
java
// ❌ 방식 1: 필드 주입 (Field Injection) – 편하지만 권장하지 않음
@Service
public class OrderService {
@Autowired
private UserRepository userRepository; // 필드에 직접 주입
// 단점:
// - 테스트 시 Mock 주입 어려움
// - 순환 참조 감지 어려움
// - 객체 불변성 보장 불가 (final 사용 불가)
}
// ⚠️ 방식 2: Setter 주입 (Setter Injection) – 선택적 의존성에 사용
@Service
public class OrderService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 단점: 객체 생성 후 주입 → 주입 전 메서드 호출 시 NullPointerException 위험
}
// ✅ 방식 3: 생성자 주입 (Constructor Injection) – 공식 권장 방식
@Service
@RequiredArgsConstructor // Lombok이 생성자 자동 생성 (@Autowired 생략 가능)
public class OrderService {
private final UserRepository userRepository; // final 선언 가능
private final ProductRepository productRepository;
// 장점:
// - 불변성 보장 (final 키워드)
// - 테스트 시 Mock 주입 용이 (new OrderService(mockRepo))
// - 순환 참조 시 애플리케이션 구동 단계에서 즉시 감지
// - 의존성 명시적으로 드러남
}
왜 생성자 주입을 써야 하는가:
java
// 생성자 주입의 테스트 용이성 확인
class OrderServiceTest {
@Test
void 주문_저장_테스트() {
// Mock 객체를 생성자로 직접 주입 → @SpringBootTest 없이도 단위 테스트 가능
UserRepository mockUserRepo = mock(UserRepository.class);
ProductRepository mockProductRepo = mock(ProductRepository.class);
OrderService orderService = new OrderService(mockUserRepo, mockProductRepo);
// 테스트 로직...
}
}
⑤ @Component, @Service, @Repository – Bean 등록 애노테이션 삼형제
세 애노테이션 모두 @Component를 기반으로 하며, 스프링 컨테이너에 Bean을 등록하는 역할을 합니다. 기능은 동일하지만 역할을 명시해 가독성과 AOP 처리에 차이를 둡니다.
java
@Component // 일반적인 컴포넌트 (어느 레이어인지 불명확할 때)
public class EmailValidator { ... }
@Service // 비즈니스 로직을 담당하는 서비스 레이어
public class PaymentService { ... }
@Repository // 데이터 접근 레이어 (JPA, MyBatis 등)
// 추가 기능: 데이터 접근 예외를 스프링 DataAccessException으로 변환
public class UserRepositoryImpl { ... }
| 애노테이션 | 의미 | 추가 기능 |
|---|---|---|
@Component | 일반 컴포넌트 | 없음 |
@Service | 비즈니스 서비스 | 없음 (의미론적 구분) |
@Repository | 데이터 접근 객체 | DB 예외 → 스프링 예외 자동 변환 |
@Controller | MVC 컨트롤러 | 뷰(View) 반환 처리 |
@RestController | REST API 컨트롤러 | @Controller + @ResponseBody |
5. 데이터와 트랜잭션 – @Entity, @Repository, @Transactional
⑥ @Entity – JPA 엔티티 선언
해당 클래스가 데이터베이스 테이블과 매핑되는 **JPA 엔티티(Entity)**임을 선언합니다. 스프링부트 + JPA(Hibernate) 환경에서 가장 자주 만나는 애노테이션 중 하나입니다.
java
@Entity // 이 클래스가 DB 테이블과 매핑됨을 선언
@Table(name = "users") // 매핑할 테이블 이름 지정 (생략 시 클래스명)
@Getter // Lombok
@NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA 기본 생성자 (protected)
public class User {
@Id // 기본 키(Primary Key) 매핑
@GeneratedValue(strategy = GenerationType.IDENTITY) // AUTO_INCREMENT
private Long id;
@Column(nullable = false, length = 100) // 컬럼 제약 조건
private String name;
@Column(unique = true, nullable = false)
private String email;
@Enumerated(EnumType.STRING) // Enum → DB에 문자열로 저장
private UserStatus status;
@CreatedDate // Spring Data JPA 감사 기능
@Column(updatable = false)
private LocalDateTime createdAt;
// 팩토리 메서드 패턴 (생성자 대신 사용 권장)
public static User create(String name, String email) {
User user = new User();
user.name = name;
user.email = email;
user.status = UserStatus.ACTIVE;
return user;
}
}
실무 주의사항:
JPA 엔티티의 기본 생성자는 public 또는 protected여야 합니다. private으로 선언하면 JPA가 프록시 객체를 생성할 수 없어 오류가 발생합니다. Lombok의 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하면 외부에서 직접 생성을 막으면서 JPA 요건도 충족합니다.
⑦ @Transactional – 트랜잭션 경계 선언
메서드 또는 클래스에 선언하여 트랜잭션 시작·커밋·롤백을 자동으로 관리합니다. 스프링이 AOP(관점 지향 프로그래밍)를 통해 트랜잭션 처리 코드를 메서드 주변에 자동으로 삽입합니다.
java
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
private final PaymentService paymentService;
// ✅ 올바른 @Transactional 사용
@Transactional // 메서드 전체가 하나의 트랜잭션
public Order placeOrder(OrderRequest request) {
// 1. 재고 차감
inventoryService.decrease(request.getProductId(), request.getQty());
// 2. 주문 저장
Order order = Order.create(request);
orderRepository.save(order);
// 3. 결제 처리
paymentService.charge(request.getUserId(), order.getTotalAmount());
return order;
// → 메서드 정상 종료 시 자동 COMMIT
// → 예외 발생 시 자동 ROLLBACK (1, 2, 3 모두 취소)
}
// 읽기 전용 트랜잭션 – 성능 최적화
@Transactional(readOnly = true) // Dirty Checking 비활성화 → 성능 향상
public List<Order> findUserOrders(Long userId) {
return orderRepository.findByUserId(userId);
}
}
@Transactional 핵심 속성:
| 속성 | 기본값 | 설명 |
|---|---|---|
readOnly | false | true 시 읽기 전용 (성능 최적화) |
rollbackFor | RuntimeException | 롤백 대상 예외 클래스 |
noRollbackFor | — | 롤백 제외 예외 클래스 |
timeout | -1 (무제한) | 트랜잭션 타임아웃(초) |
propagation | REQUIRED | 트랜잭션 전파 전략 |
isolation | DEFAULT | 트랜잭션 격리 수준 |
실무 최대 주의사항 – @Transactional이 동작하지 않는 경우:
java
@Service
public class UserService {
// ❌ 같은 클래스 내부 메서드 호출 – @Transactional 무시됨!
public void process() {
this.save(); // 내부 호출: 스프링 프록시를 거치지 않음 → 트랜잭션 적용 안 됨
}
@Transactional
public void save() {
// 이 메서드를 같은 클래스에서 내부 호출 시 트랜잭션 동작 안 함
}
// ❌ private 메서드 – @Transactional 무시됨!
@Transactional
private void savePrivate() {
// private 메서드에는 AOP 프록시 적용 불가
}
}
스프링의 @Transactional은 AOP 프록시를 통해 동작하기 때문에, 같은 클래스 내부에서 직접 메서드를 호출하거나 private 메서드에 선언하면 트랜잭션이 적용되지 않습니다. 반드시 외부 빈(Bean)을 통해 호출되어야 합니다.
⑧ @Query – JPQL·네이티브 SQL 직접 작성
Spring Data JPA의 JpaRepository에서 자동 생성 쿼리로 표현하기 어려운 복잡한 쿼리를 직접 작성할 때 사용합니다.
java
public interface OrderRepository extends JpaRepository<Order, Long> {
// 메서드 이름으로 자동 쿼리 생성 (간단한 조회)
List<Order> findByUserIdAndStatus(Long userId, OrderStatus status);
// @Query – JPQL (엔티티 기반)
@Query("SELECT o FROM Order o WHERE o.userId = :userId AND o.createdAt >= :from")
List<Order> findRecentOrders(
@Param("userId") Long userId,
@Param("from") LocalDateTime from);
// @Query – 네이티브 SQL (복잡한 집계·성능 최적화)
@Query(value = """
SELECT DATE(created_at) AS date,
COUNT(*) AS count,
SUM(amount) AS total
FROM orders
WHERE user_id = :userId
GROUP BY DATE(created_at)
ORDER BY date DESC
LIMIT 30
""", nativeQuery = true)
List<Object[]> findDailyOrderStats(@Param("userId") Long userId);
// 벌크 업데이트 (@Modifying 필수)
@Modifying
@Query("UPDATE Order o SET o.status = :status WHERE o.id IN :ids")
int bulkUpdateStatus(@Param("ids") List<Long> ids, @Param("status") OrderStatus status);
}
6. 설정과 예외 처리 – @Value, @Configuration, @ExceptionHandler
⑨ @Value – 프로퍼티 값 주입
application.yml 또는 application.properties에 정의된 설정값을 Java 코드에 주입합니다. 환경에 따라 달라지는 값(API 키, 서버 URL, 타임아웃 등)을 코드에서 분리해 관리할 수 있습니다.
yaml
# application.yml
app:
jwt:
secret: mySecretKey2026
expiration: 86400000
external:
api-url: https://api.example.com
timeout: 5000
pagination:
default-size: 20
java
@Service
public class JwtService {
@Value("${app.jwt.secret}")
private String secretKey;
@Value("${app.jwt.expiration}")
private long expirationMs;
// 기본값 설정 (프로퍼티 없을 때 사용)
@Value("${app.pagination.default-size:10}") // 없으면 10 사용
private int defaultPageSize;
// 환경변수도 주입 가능
@Value("${SERVER_PORT:8080}")
private int serverPort;
public String generateToken(String userId) {
return Jwts.builder()
.subject(userId)
.expiration(new Date(System.currentTimeMillis() + expirationMs))
.signWith(Keys.hmacShaKeyFor(secretKey.getBytes()))
.compact();
}
}
@Value vs @ConfigurationProperties 선택 기준:
java
// @Value – 개별 값 주입 (소수의 설정값)
@Value("${app.jwt.secret}")
private String secretKey;
// @ConfigurationProperties – 그룹 설정 주입 (관련 설정이 여러 개일 때 권장)
@Component
@ConfigurationProperties(prefix = "app.jwt")
@Getter @Setter
public class JwtProperties {
private String secret;
private long expiration;
// → application.yml의 app.jwt.* 값이 모두 자동 매핑됨
}
⑩ @ExceptionHandler와 @RestControllerAdvice – 전역 예외 처리
애플리케이션 전체에서 발생하는 예외를 한 곳에서 중앙 집중적으로 처리합니다. 컨트롤러마다 try-catch를 반복 작성하는 대신, @RestControllerAdvice 클래스 하나로 모든 예외를 깔끔하게 처리합니다.
java
// 통일된 에러 응답 형식
@Getter
@AllArgsConstructor
public class ErrorResponse {
private final int status;
private final String code;
private final String message;
private final LocalDateTime timestamp;
public static ErrorResponse of(int status, String code, String message) {
return new ErrorResponse(status, code, message, LocalDateTime.now());
}
}
// 전역 예외 처리기
@RestControllerAdvice // @ControllerAdvice + @ResponseBody
@Slf4j
public class GlobalExceptionHandler {
// 비즈니스 예외 처리
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
log.warn("[404] 사용자를 찾을 수 없음: {}", e.getMessage());
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(ErrorResponse.of(404, "USER_NOT_FOUND", e.getMessage()));
}
// 유효성 검사 실패 처리 (@Valid 위반)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException e) {
String message = e.getBindingResult()
.getFieldErrors()
.stream()
.map(err -> err.getField() + ": " + err.getDefaultMessage())
.collect(Collectors.joining(", "));
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ErrorResponse.of(400, "VALIDATION_FAILED", message));
}
// 인증 실패 처리
@ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ErrorResponse> handleUnauthorized(UnauthorizedException e) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(ErrorResponse.of(401, "UNAUTHORIZED", e.getMessage()));
}
// 그 외 모든 예외 (최후 방어선)
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("[500] 예상치 못한 오류 발생", e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ErrorResponse.of(500, "INTERNAL_ERROR", "서버 내부 오류가 발생했습니다."));
}
}
보너스 – @Configuration과 @Bean
스프링이 자동 설정할 수 없는 서드파티 라이브러리나 복잡한 객체를 수동으로 Bean 등록할 때 사용합니다.
java
@Configuration // 이 클래스가 Bean 설정 클래스임을 선언
public class AppConfig {
// @Bean – 메서드가 반환하는 객체를 스프링 Bean으로 등록
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(3000);
factory.setReadTimeout(10000);
return new RestTemplate(factory);
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
// 환경별 Bean 조건부 등록
@Bean
@Profile("prod") // 운영 환경에서만 등록
public DataSource prodDataSource() { ... }
@Bean
@Profile("dev") // 개발 환경에서만 등록
public DataSource devDataSource() { ... }
}
핵심 10개 애노테이션 한눈에 보기
[스프링부트 핵심 애노테이션 10개 요약]
🟢 구동·설정 레이어
① @SpringBootApplication → 앱 진입점, 컴포넌트 스캔 + 자동 설정 활성화
② @Configuration + @Bean → 수동 Bean 등록 및 커스텀 설정
🔵 웹(API) 레이어
③ @RestController → REST API 컨트롤러 (JSON 응답 자동화)
④ @RequestMapping 계열 → URL 라우팅 (Get/Post/Put/Patch/DeleteMapping)
🟡 서비스·Bean 레이어
⑤ @Service / @Component → 비즈니스 로직 Bean 등록
⑥ @Autowired (생성자 주입) → 의존성 자동 주입
🔴 데이터 레이어
⑦ @Entity → JPA DB 테이블 매핑
⑧ @Repository + @Query → 데이터 접근, 커스텀 JPQL/SQL
⑨ @Transactional → 트랜잭션 자동 관리
🟣 공통·횡단 관심사
⑩ @RestControllerAdvice → 전역 예외 처리 (+ @ExceptionHandler)
+ @Value → 프로퍼티 설정값 주입
결론
스프링부트 애노테이션 10가지는 단순히 외우는 것이 아니라, 각각이 스프링 컨테이너와 어떻게 상호작용하는지 원리를 이해해야 실무에서 제대로 활용할 수 있습니다. @SpringBootApplication으로 시작해 웹 레이어의 @RestController, 서비스 레이어의 생성자 주입 @Autowired, 데이터 레이어의 @Entity와 @Transactional, 그리고 설정·예외 처리까지 각 레이어별로 역할이 명확히 나뉩니다. 오늘 정리한 내용을 바탕으로 현재 진행 중인 프로젝트 코드를 다시 한번 훑어보고, 혹시 필드 주입이나 트랜잭션 내부 호출 같은 실수가 없는지 점검해 보세요.
지금 바로 예제 코드를 스프링 이니셜라이저(start.spring.io)에서 새 프로젝트를 만들어 직접 타이핑해보는 것이 가장 빠른 학습법입니다.
답글 남기기