자바 웹 예외처리

Dear·2025년 6월 25일

TIL

목록 보기
48/74

💙 상황별 예외 페이지 작성(web.xml)

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>

💙 500(Internal Server Error)를 Spring으로 처리

1. ExceptionController 클래스 작성

@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" 객체를 전달한다.

2. ExceptionController를 Spring Container에 등록

<context:property-placeholder location="/WEB-INF/spring/settings.properties" />

<bean id="exceptionController"
      class="com.hello.ExceptionController">
    <!-- 필요할 경우 Bean 주입 가능 -->
</bean>

<import resource="./controllers.xml" />

💙 Transaction과 AOP

서비스 로직 수행 중 오류가 발생하면 DB에 반영된 변경 상항을 되돌려야 한다.
돈이 지불됐는데 주문은 실패한 상태가 되면 안되므로 트랜잭션 복구(Rollback)이 필요하다.

AOP를 통한 트랜잭션 처리

Spring에서는 AOP(관점 지향 프로그래밍)를 통해 예외 발생 시 자동으로 Rollback,
예외 없이 성공적으로 끝났을 때는 Commit 처리를 할 수 있게 해준다.

@Service
@Transactional  // 이게 AOP 기반으로 동작하는 설정(내부적으로 AOP로 작동)
public class OrderService {

    public void placeOrder(OrderRequest request) {
        saveOrderToDB();     // 주문 저장
        chargeUser();        // 사용자 결제
        notifyDelivery();    // 배송 요청
        // 중간에 예외가 발생하면 모든 DB 작업은 rollback 됨
    }
}

AOP

특정 패키지 또는 클래스 내에서 특정 상황이 발생했을 때, 외부 코드가 자동으로 개입한다.
메소드 안의 주기능과 보조 기능을 분리한 후 선택적으로 메소드에 적용해서 사용한다.

용어설명
aspect구현하고자 하는 보조기능
adviceaspect의 실제 구현체(클래스)를 의미. 메소드 호출을 기준으로 여러 지점에서 실행
joinpointadvice를 적용하는 지점. 스프링은 method 결합점만 제공
pointcutadvice가 적용되는 대상을 지정. 패키지이름/클래스이름/메소드이름을 정규식으로 지정하여 사용
targetadvice가 적용되는 클래스를 의미
weavingadvice를 주기능에 적용하는 것을 의미

어노테이션 기반 AOP 사용 설정 (스프링 3 이상)

  1. @EnableAspectJAutoProxy 활성화
    AOP 기능을 사용할 수 있도록 Spring에서 '자동 프록시 생성'을 활성화하는 설정용 어노테이션이다.
    AOP 기반의 어노테이션(@Aspect, @Before, @After 등)을 인식할 수 있도록 스프링에게 AOP 사용시작을 알려주는 역할을 한다.
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    // AOP 관련 설정
}
  1. 의존성 추가 (Maven)
<!-- AspectJ -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>사용 중인 Spring 버전에 맞춰 작성</version>
</dependency>
  1. Aspect 클래스 정의
@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeServiceMethods() {
        System.out.println("서비스 메서드 실행 전 로그 찍기!");
    }
}

XML 기반 AOP 설정

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 정의용 어노테이션

@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 어노테이션을 사용하는 구조

💙 파라미터 검증 (Validation Check)

사용자 입력 값이나 데이터 객체에 대한 유효성 검사
유효성 검사를 통해 잘못된 데이터가 서버나 DB로 전달되는 것을 방지할 수 있다.

-> 이메일 주소 형식이 맞는지
-> 이름이 비어 있지 않은지
-> 비밀번호 길이가 최소 8자리 이상인지

요청 파라미터 값을 검사하는 방법

  • 웹 브라우저 : 자바스크립트를 이용해서 데이터를 웹 서버에 전송하기 전에 미리 검사한다.
  • 웹 서버 : 전달받은 요청 파라미터의 값을 검사한다.
    파라미터 값이 올바르지 않을 경우 에러 코드를 응답하거나 재입력을 위한 폼 화면을 웹 브라우저에 전송한다.

스프링에서는 javax.validation 또는 jakarta.validation 기반의 Bean Validation을 통해 처리한다.

작동 순서(Spring + Hibernate Validator)

  1. hibernate-validator 라이브러리를 의존성으로 추가
  2. LocalValidatorFactoryBean을 등록하여 Spring이 유효성 검사 기능을 사용할 수 있도록 설정
  3. DTO나 폼 클래스에 어노테이션으로 제약 조건을 명시
  4. @Valid 또는 @Validated 어노테이션으로 컨트롤러 단에서 자동으로 검증 수행
  5. 오류 발생 시 BindingResult를 통해 에러 정보를 수집하고 처리

주요 어노테이션

어노테이션설명
@NotNullnull이면 안 됨
@NotEmptynull 또는 빈 문자열 불가
@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처럼 외부 입력을 받는 시스템에서는 반드시 설정해야 한다.

profile
친애하는 개발자

0개의 댓글