HTTP 오류 코드 404와 500이 발생했을 때 각각의 JSP 파일로 에러 페이지를 보여주도록 지정
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/view/errors/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/view/errors/500.jsp</location>
</error-page>
@ControllerAdvice("com.hello")
public class ExceptionController {
@ExceptionHandler(RuntimeException.class)
public ModelAndView view500Page(RuntimeException e) {
ModelAndView view = new ModelAndView();
view.setViewName("errors/500");
view.addObject("message", "에러가 발생했습니다. 잠시 후 다시 시도해주세요.");
return view;
}
}
RuntimeException이 발생했을 때 "errors/500" 뷰로 이동하고 "message" 객체를 전달한다.
<context:property-placeholder location="/WEB-INF/spring/settings.properties" />
<bean id="exceptionController"
class="com.hello.ExceptionController">
<!-- 필요할 경우 Bean 주입 가능 -->
</bean>
<import resource="./controllers.xml" />
서비스 로직 수행 중 오류가 발생하면 DB에 반영된 변경 상항을 되돌려야 한다.
돈이 지불됐는데 주문은 실패한 상태가 되면 안되므로 트랜잭션 복구(Rollback)이 필요하다.
Spring에서는 AOP(관점 지향 프로그래밍)를 통해 예외 발생 시 자동으로 Rollback,
예외 없이 성공적으로 끝났을 때는 Commit 처리를 할 수 있게 해준다.
@Service
@Transactional // 이게 AOP 기반으로 동작하는 설정(내부적으로 AOP로 작동)
public class OrderService {
public void placeOrder(OrderRequest request) {
saveOrderToDB(); // 주문 저장
chargeUser(); // 사용자 결제
notifyDelivery(); // 배송 요청
// 중간에 예외가 발생하면 모든 DB 작업은 rollback 됨
}
}
특정 패키지 또는 클래스 내에서 특정 상황이 발생했을 때, 외부 코드가 자동으로 개입한다.
메소드 안의 주기능과 보조 기능을 분리한 후 선택적으로 메소드에 적용해서 사용한다.
| 용어 | 설명 |
|---|---|
| aspect | 구현하고자 하는 보조기능 |
| advice | aspect의 실제 구현체(클래스)를 의미. 메소드 호출을 기준으로 여러 지점에서 실행 |
| joinpoint | advice를 적용하는 지점. 스프링은 method 결합점만 제공 |
| pointcut | advice가 적용되는 대상을 지정. 패키지이름/클래스이름/메소드이름을 정규식으로 지정하여 사용 |
| target | advice가 적용되는 클래스를 의미 |
| weaving | advice를 주기능에 적용하는 것을 의미 |
@EnableAspectJAutoProxy 활성화@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
// AOP 관련 설정
}
<!-- AspectJ -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>사용 중인 Spring 버전에 맞춰 작성</version>
</dependency>
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeServiceMethods() {
System.out.println("서비스 메서드 실행 전 로그 찍기!");
}
}
AOP 네임스페이스 추가
applicationContext.xml, root-context.xml, servlet-context.xml 등에 작성
<beans xmlns:aop="http://www.springframework.org/schema/aop"
... >
<aop:aspectj-autoproxy />
<bean id="loggingAspect" class="com.example.LoggingAspect" />
</beans>
@Aspect로 정의된 클래스 안에서 AOP 로직을 작성할 때 사용한다.
-> AOP 동작을 실제로 구현하는 기능성 어노테이션들
| 어노테이션 | 역할 설명 |
|---|---|
@Aspect | 이 클래스가 AOP 보조 기능(=Aspect)을 가진 클래스임을 나타냄 |
@Before | 대상 메서드 실행 이전에 advice 실행 |
@After | 대상 메서드 실행 이후(성공/실패 무관) 에 advice 실행 |
@AfterReturning | 메서드가 정상적으로 끝났을 때만 advice 실행 |
@AfterThrowing | 메서드 실행 중 예외가 발생했을 때만 advice 실행 |
@Around | 메서드 실행 전후 전체를 감싸서 조작 (가장 강력함) |
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("서비스 메서드 실행 전");
}
}
@EnableAspectJAutoProxy를 먼저 설정한 다음에,
@Aspect가 붙은 클래스 안에서 @Before, @After 같은 AOP 어노테이션을 사용하는 구조
사용자 입력 값이나 데이터 객체에 대한 유효성 검사
유효성 검사를 통해 잘못된 데이터가 서버나 DB로 전달되는 것을 방지할 수 있다.
-> 이메일 주소 형식이 맞는지
-> 이름이 비어 있지 않은지
-> 비밀번호 길이가 최소 8자리 이상인지
스프링에서는 javax.validation 또는 jakarta.validation 기반의 Bean Validation을 통해 처리한다.
hibernate-validator 라이브러리를 의존성으로 추가LocalValidatorFactoryBean을 등록하여 Spring이 유효성 검사 기능을 사용할 수 있도록 설정@Valid 또는 @Validated 어노테이션으로 컨트롤러 단에서 자동으로 검증 수행BindingResult를 통해 에러 정보를 수집하고 처리| 어노테이션 | 설명 |
|---|---|
@NotNull | null이면 안 됨 |
@NotEmpty | null 또는 빈 문자열 불가 |
@NotBlank | 공백 문자도 허용하지 않음 |
@Size(min=, max=) | 문자열, 컬렉션 크기 제한 |
@Email | 이메일 형식 검증 |
@Min, @Max | 숫자의 최소/최대값 제한 |
@Pattern | 정규표현식 패턴 검사 |
public class UserDto {
@NotBlank
private String name;
@Email
private String email;
@Size(min = 8, message = "비밀번호는 8자 이상이어야 합니다.")
private String password;
}
@PostMapping("/signup")
public ResponseEntity<?> signup(@Valid @RequestBody UserDto dto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().body(bindingResult.getAllErrors());
}
return ResponseEntity.ok("가입 완료");
}
이번 학습에서는 스프링의 핵심 기능 중 하나인 AOP와 트랜잭션 처리 방식, 예외 처리 흐름, 그리고 입력 데이터의 유효성 검증에 대해 정리했다.
예외 처리는 단순히 에러 메시지를 출력하는 것을 넘어서, 사용자 경험에 직접적인 영향을 줄 수 있어 중요한 부분이다. AOP는 중복되는 로직을 분리할 수 있다는 점에서 코드의 유지보수성과 가독성을 높이는 데 매우 유용하다. 트랜잭션 처리도 단순히 @Transactional만 붙이면 되는게 아니라, 예외의 종류나 롤백 조건에 따라 동작이 달라질 수 있어서 더 신중하게 사용해야 한다. 유효성 검사는 서버 단에서 최소한의 방어선 역할을 하며, 특히 REST API처럼 외부 입력을 받는 시스템에서는 반드시 설정해야 한다.