Bootcamp/Fundamentals

Spring 심화

개발자 오리 2026. 6. 26. 15:04

💡  오늘 학습 키워드

  • 단위 테스트
  • 통합 테스트
  • AOP
  • 예외 처리

 

🎯  학습 내용 정리

1. 단위 테스트

소프트웨어는 개발 이후 QA 테스트를 거쳐 실제 운영 환경에 배포된다. 하지만 버그를 늦게 발견할수록 수정 비용은 기하급수적으로 증가한다. 따라서 개발 단계에서 미리 테스트를 작성하면 오류를 빠르게 발견하고 수정 비용을 크게 줄일 수 있다.

 

단위 테스트Unit Test라고도 하며 애플리케이션을 가장 작은 단위로 나누어 각각의 기능이 정상적으로 동작하는지 검증하는 테스트 기법이다.

 

단위 테스트의 장점은 다음과 같다.

  • 문제가 발생한 위치를 빠르게 찾을 수 있다.
  • 테스트 실행 속도가 빠르다.
  • 리팩토링 이후에도 기존 기능이 정상 동작하는지 검증할 수 있다.
  • 코드의 신뢰성을 높일 수 있다.

 

 

JUnit5Java에서 가장 많이 사용하는 단위 테스트 프레임워크이다.

테스트 실행부터 검증, 반복 테스트, 예외 테스트 등 다양한 기능을 제공한다.

 

 

JUnit5는 테스트 실행 전후에 공통 작업을 수행할 수 있는 Annotation을 제공한다.

 

@BeforeEach

각 테스트가 실행되기 전에 수행된다.

@BeforeEach
void setUp() {
    calculator = new Calculator();
}

주로 객체 생성이나 공통 데이터 초기화에 사용한다.

 

@AfterEach

각 테스트가 종료된 후 실행된다.

@AfterEach
void tearDown() {
}

테스트 종료 후 정리 작업에 사용된다.

 

@BeforeAll

모든 테스트가 시작되기 전에 단 한 번만 실행된다.

@BeforeAll
static void beforeAll() {
}

static 메서드로 작성해야 한다.

 

@AfterAll

모든 테스트가 끝난 후 마지막에 실행된다.

@AfterAll
static void afterAll() {
}

마찬가지로 static 메서드여야 한다.

 

 

또한, 테스트를 보기 쉽게 만드는 Annotation도 존재한다.

 

@DisplayName

테스트 이름을 사람이 읽기 쉬운 형태로 지정할 수 있다.

@Test
@DisplayName("계산기 덧셈 테스트")
void test() {

}

실행 결과만 봐도 어떤 테스트인지 쉽게 확인할 수 있다.

 

@Nested

관련 있는 테스트를 그룹으로 묶을 수 있다.

@Nested
class LoginTest {

}

@Nested
class SignUpTest {

}

기능별로 테스트를 정리하기 때문에 테스트 코드의 가독성이 좋아진다.

 

@Order

테스트 실행 순서를 지정할 수 있다.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

@Order(1)
@Test
void test1() {

}

기본적으로 테스트는 실행 순서를 보장하지 않기 때문에 필요한 경우에만 사용하는 것이 좋다.

 

 

반복 테스트 관련 Annotation도 존재한다.

 

@RepeatedTest

동일한 테스트를 여러 번 실행할 수 있다.

@RepeatedTest(5)
void repeatTest() {

}

랜덤 값이나 반복 검증이 필요한 경우 유용하다.

 

@ParameterizedTest

다양한 입력값으로 동일한 테스트를 수행할 수 있다.

@ParameterizedTest
@ValueSource(ints = {1,2,3,4,5})
void test(int num) {

}

하나의 테스트 코드로 여러 케이스를 검증할 수 있어 중복을 줄일 수 있다.

 

 

Assertions테스트 결과가 기대한 값과 일치하는지 검증하는 기능이다.

대표적으로 다음 메서드들이 사용된다.

 

assertEquals

예상값과 실제값이 같은지 확인한다.

assertEquals(expected, actual);

 

assertNotEquals

두 값이 서로 다른지 확인한다.

assertNotEquals(expected, actual);

 

assertTrue / assertFalse

조건이 true 또는 false인지 확인한다.

assertTrue(result);
assertFalse(result);

 

assertNull / assertNotNull

객체가 null인지 여부를 검증한다.

assertNull(result);
assertNotNull(result);

 

assertThrows

예외가 발생하는지 검증한다.

IllegalArgumentException exception =
    assertThrows(
        IllegalArgumentException.class,
        () -> calculator.operate(5, "?", 2)
    );

예외 메시지까지 함께 검증할 수도 있다.

assertEquals("잘못된 연산자입니다.", exception.getMessage());

 

 

 

JUnit 테스트는 일반적으로 Given-When-Then 패턴을 사용한다.

 

Given

테스트에 필요한 데이터를 준비한다.

int num1 = 5;
int num2 = 2;

 

When

테스트 대상 메서드를 실행한다.

Double result = calculator.operate(num1, "/", num2);

 

Then

실행 결과를 검증한다.

assertEquals(2.5, result);

이 구조를 사용하면 테스트의 의도가 명확해지고 유지보수가 쉬워진다.

 

 

다음과 같이 ProductService를 테스트하려고 하면 문제가 발생한다.

ProductService productService = new ProductService();

실제로 ProductService는 여러 Repository를 생성자로 전달받는다.

ProductRepository
FolderRepository
ProductFolderRepository

따라서 단순히 객체를 생성할 수 없다.

 

또한 updateProduct() 내부에서는

productRepository.findById(id)

처럼 실제 데이터베이스를 조회한다.

 

즉,

  • Repository가 필요하고
  • DB도 필요하다.

이렇게 되면 Service 하나만 테스트하는 것이 아니라 Repository와 DB까지 함께 테스트하게 된다.

 

단위 테스트의 목적은 하나의 클래스만 독립적으로 테스트하는 것이다. 이를 위해 사용하는 것이 Mock 객체이다.

Mock은 실제 객체와 동일한 인터페이스를 가지지만 실제 동작은 수행하지 않는다.

 

예를 들어 Repository 대신 Mock Repository를 사용하면

findById()

호출 시 실제 DB를 조회하지 않고 미리 지정한 값을 반환한다.

 

즉,

  • DB 연결 없음
  • SQL 실행 없음
  • 원하는 결과만 반환

이렇게 Service 로직만 순수하게 테스트할 수 있다.

 

 

MockitoMock 객체를 쉽게 생성해주는 프레임워크이다.

먼저 Mockito를 사용할 수 있도록 설정한다.

@ExtendWith(MockitoExtension.class)

그리고 Repository를 Mock으로 선언한다.

@Mock
ProductRepository productRepository;

@Mock
FolderRepository folderRepository;

@Mock
ProductFolderRepository productFolderRepository;

이제 실제 Repository 대신 Mock 객체가 주입된다.

 

 

Mock은 단순히 선언만 하면 아무 동작도 하지 않는다.

따라서 어떤 메서드가 호출되면 무엇을 반환할지 직접 정의해야 한다.

given(productRepository.findById(productId))
    .willReturn(Optional.of(product));

이 코드는 "productRepository.findById(productId)가 호출되면 Optional.of(product)를 반환한다" 라는 의미이다.

즉, 실제 DB를 조회하지 않아도 원하는 데이터를 얻을 수 있다.

 

 

JUnit5는 테스트를 체계적으로 작성할 수 있도록 다양한 기능을 제공하며, Assertions와 Given-When-Then 패턴을 활용하면 읽기 쉬운 테스트 코드를 작성할 수 있다. 하지만 Service 계층은 Repository와 데이터베이스에 의존하기 때문에 순수한 단위 테스트를 작성하기 어렵다. 이때 Mockito의 Mock 객체를 사용하면 외부 의존성을 제거하고 필요한 동작만 정의하여 Service 로직 자체만 독립적으로 검증할 수 있다.

 

결국 단위 테스트의 핵심은 외부 환경과 분리된 상태에서 하나의 기능을 신뢰성 있게 검증하는 것이며, JUnit5와 Mockito는 이를 위한 가장 대표적인 도구이다.

 

 

2. 통합 테스트

애플리케이션은 여러 계층과 객체가 서로 협력하여 동작한다. 테스트 역시 검증 범위에 따라 크게 단위 테스트와 통합 테스트로 구분된다.

두 테스트 모두 애플리케이션의 품질을 높이기 위한 중요한 역할을 하지만, 검증 대상과 목적에는 차이가 있다.

이번에는 통합 테스트에 대해서 알아보자.

 

통합 테스트Integration Test라고도 하며 여러 컴포넌트가 함께 동작하는 과정을 검증하는 테스트이다.

Controller, Service, Repository가 실제로 연결되어 동작하는지, 데이터베이스와의 연동 과정에서 문제가 없는지를 확인할 수 있다.

즉, 각각의 단위 테스트에서는 발견하기 어려운 컴포넌트 간의 연결 문제를 검증하는 것이 목적이다.

 

통합 테스트의 특징은 다음과 같다.

  • 여러 계층을 함께 테스트한다.
  • 실제 Spring 컨테이너를 사용한다.
  • 실제 Repository와 데이터베이스를 사용할 수 있다.
  • 객체 간의 연결과 의존성을 검증할 수 있다.
  • 단위 테스트보다 실행 시간이 오래 걸린다.

따라서 통합 테스트는 애플리케이션이 실제 환경과 유사한 방식으로 정상 동작하는지 확인하는 테스트라고 할 수 있다.

 

 

Spring Boot에서는 @SpringBootTest을 사용하여 통합 테스트를 수행할 수 있다.

@SpringBootTest
class ProductServiceTest {

}

@SpringBootTest가 적용되면 테스트 실행 시 Spring 컨테이너가 함께 실행된다.

 

따라서 실제 애플리케이션과 동일한 환경에서 테스트를 수행할 수 있으며, Spring이 제공하는 다양한 기능도 사용할 수 있다.

대표적으로 다음 기능들을 사용할 수 있다.

  • Spring IoC 컨테이너
  • 의존성 주입(DI)
  • Repository Bean 주입
  • 실제 데이터베이스 CRUD
  • 트랜잭션 처리

즉, 테스트 환경에서도 실제 애플리케이션과 거의 동일한 실행 환경을 구성할 수 있다.

 

 

단위 테스트와 통합 테스트 비교를 해보면 다음과 같다.

구분 단위 테스트 통합 테스트
테스트 대상 하나의 클래스 또는 메서드 여러 컴포넌트
Spring 실행 실행하지 않음 실행함
DB 사용 사용하지 않음(Mock 사용) 실제 DB 사용 가능
실행 속도 빠름 상대적으로 느림
검증 범위 비즈니스 로직 컴포넌트 간 연동
목적 기능 자체 검증 시스템 전체 동작 검증

 

 

그렇다면 언제 어떤 테스트를 사용해야 할까?

 

단위 테스트와 통합 테스트는 서로 대체하는 관계가 아니라 상호 보완적인 관계이다. 비즈니스 로직의 정확성을 빠르게 검증하고 싶다면 단위 테스트가 적합하다. 반면 객체 간 연결이나 데이터베이스 연동까지 포함한 전체 동작을 확인하려면 통합 테스트가 필요하다.

 

실무에서는 일반적으로 단위 테스트를 많이 작성하여 빠르게 로직을 검증하고, 핵심 기능에 대해서는 통합 테스트를 추가하여 전체 시스템이 정상적으로 동작하는지 확인하는 방식으로 함께 활용한다.

 

 

3. AOP

애플리케이션에는 핵심 비즈니스 로직 외에도 로깅, 실행 시간 측정, 권한 검사, 트랜잭션 처리처럼 여러 곳에서 반복되는 공통 기능이 존재한다. 이러한 기능을 각 클래스마다 직접 작성하면 중복 코드가 많아지고 유지보수가 어려워진다.

 

Spring AOPAspect Oriented Programming의 약자로 이러한 공통 관심사(Cross Cutting Concern) 를 별도의 클래스로 분리하여 필요한 위치에 자동으로 적용할 수 있도록 지원한다.

이를 통해 비즈니스 로직은 핵심 기능에만 집중할 수 있고, 공통 기능은 한 곳에서 관리할 수 있다.

 

 

Spring AOP 주요 Annotation은 다음과 같다.

 

@Aspect

@Aspect는 해당 클래스가 AOP 기능을 수행하는 클래스임을 나타내는 Annotation이다.

Spring Bean으로 등록된 클래스에만 적용할 수 있으며 일반적으로 @Component와 함께 사용한다.

@Aspect
@Component
public class UseTimeAop {
    ...
}

 

 

AOP에서 Advice언제 공통 기능을 실행할 것인지를 정의한다.

 

@Before

핵심 기능이 실행되기 전에 수행된다.

주로 입력값 검증이나 권한 검사 등에 사용된다.

@Before(...)

 

@After

메서드의 성공 여부와 관계없이 항상 실행된다.

자바의 finally 블록과 동일한 개념이다.

@After(...)

 

@AfterReturning

메서드가 정상적으로 종료된 경우에만 실행된다.

반환값을 사용할 수도 있다.

@AfterReturning(...)

 

@AfterThrowing

메서드 실행 중 예외가 발생했을 때만 실행된다.

예외 로그를 남기거나 알림을 보내는 기능에 활용된다.

@AfterThrowing(...)

 

@Around

메서드 실행 전과 후를 모두 제어할 수 있는 가장 강력한 Advice이다.

실행 시간 측정이나 트랜잭션 처리처럼 전후 작업이 모두 필요한 경우 주로 사용된다.

@Around(...)

 

 

AOP는 모든 메서드에 적용되는 것이 아니라 Pointcut을 이용하여 원하는 메서드만 선택적으로 적용할 수 있다.

가장 많이 사용하는 방식은 execution() 표현식이다.

execution(public * com.example.controller..*(..))

이 표현식은 다음과 같은 의미를 가진다.

  • public 메서드
  • controller 패키지 및 하위 패키지
  • 모든 클래스
  • 모든 메서드
  • 모든 매개변수

 

 

execution 표현식은 다음과 같은 형태를 가진다.

execution(
    modifiers-pattern?
    return-type-pattern
    declaring-type-pattern?
    method-name-pattern(param-pattern)
    throws-pattern?
)

주로 사용하는 패턴은 다음과 같다.

 

클래스 지정

controller 패키지의 모든 클래스

com.example.controller.*

 

controller 패키지와 하위 패키지 전체

com.example.controller..

 

 

메서드 지정

특정 메서드 하나

addFolders

 

add로 시작하는 모든 메서드

add*

 

 

파라미터 지정

매개변수 없음

()

 

매개변수 1개

(*)

 

매개변수 개수와 타입 모두 상관없음

(..)

 

 

동일한 Pointcut을 여러 Advice에서 사용할 경우 @Pointcut으로 별도 정의할 수 있다.

@Pointcut("execution(* com.example.controller.ProductController.*(..))")
private void product() {}

@Pointcut("execution(* com.example.controller.FolderController.*(..))")
private void folder() {}

이후에는 메서드 이름만 이용하여 Pointcut을 조합할 수 있다.

@Around("product() || folder()")

또는

@Around("controller() && !viewController()")

처럼 논리 연산자를 이용한 조합도 가능하다.

 

 

AOP는 실제 Controller를 직접 호출하지 않는다.

Spring은 실행 시점에 프록시(Proxy) 객체를 생성하여 중간에서 요청을 가로챈다.

 

동작 순서는 다음과 같다.

클라이언트
    ↓
DispatcherServlet
    ↓
Proxy 객체
    ↓
AOP 실행
    ↓
실제 Controller

프록시는 핵심 기능을 호출하기 전후에 원하는 부가 기능을 수행한 뒤 실제 메서드를 실행한다.

핵심 메서드는 자신이 AOP의 적용 대상이라는 사실을 전혀 알지 못한 채 기존과 동일하게 동작한다.

 

실제 메서드 호출은

joinPoint.proceed();

를 통해 이루어진다.

즉, 원래 호출하려던 메서드와 전달된 매개변수를 그대로 실행해 주는 역할을 한다.

 

 

정리하자면, Spring AOP는 로깅, 실행 시간 측정, 인증, 예외 처리처럼 여러 클래스에서 반복되는 기능을 하나의 Aspect로 분리하여 관리할 수 있도록 도와준다. Advice를 통해 실행 시점을 선택하고, Pointcut으로 적용 대상을 지정하면 핵심 비즈니스 로직은 변경하지 않으면서도 필요한 공통 기능을 손쉽게 추가할 수 있다. 또한 Spring은 프록시 객체를 이용하여 AOP를 적용하므로 기존 코드의 변경 없이도 부가 기능을 자연스럽게 삽입할 수 있다.

 

 

4. 예외 처리

웹 애플리케이션에서는 예외가 발생하는 것이 자연스러운 상황이다. 중요한 것은 예외를 얼마나 일관성 있고 명확하게 처리하느냐이다.

예외 처리를 별도로 학습하는 이유는 크게 두 가지이다.

첫 번째는 클라이언트와 서버가 동일한 방식으로 오류를 이해해야 하기 때문이다. 서버에서 어떤 문제가 발생했는지 정확한 상태 코드와 메시지를 전달해야 클라이언트도 올바르게 대응할 수 있다.

두 번째는 관심사의 분리(Separation of Concerns)이다. 이전에 AOP를 이용해 공통 기능을 분리했던 것처럼 예외 처리 역시 비즈니스 로직과 분리하여 관리하면 코드의 가독성과 유지보수성이 크게 향상된다.

 

 

HTTP 응답에는 항상 상태 코드(Status Code)가 포함된다.

대표적으로 사용하는 상태 코드는 다음과 같다.

  • 2xx : 요청 성공
  • 4xx : 클라이언트의 잘못된 요청
  • 5xx : 서버 내부 오류

예를 들어 폴더 생성 API에서 이미 존재하는 폴더를 생성하려고 하면 이는 클라이언트의 잘못된 요청이므로 400(Bad Request)를 반환하는 것이 적절하다.

 

Spring에서는 이러한 상태 코드를 HttpStatus enum으로 제공한다.

HttpStatus.OK
HttpStatus.BAD_REQUEST
HttpStatus.NOT_FOUND
HttpStatus.INTERNAL_SERVER_ERROR

덕분에 숫자를 직접 사용하는 것보다 의미를 명확하게 표현할 수 있다.

 

 

예외 처리를 하지 않으면 서버 내부에서 발생한 예외가 클라이언트에게 의도하지 않은 형태로 전달될 수 있다.

예를 들어 중복 폴더 생성 시 원하는 응답은 다음과 같다.

HTTP 400 Bad Request

{
    "statusCode": 400,
    "errorMessage": "중복된 폴더명을 제거해 주세요!"
}

하지만 별도의 예외 처리가 없다면 로그인 페이지가 반환되거나 HTML 오류 화면이 내려오는 등 API 클라이언트가 처리하기 어려운 응답을 받을 수도 있다.

 

 

가장 먼저 사용할 수 있는 방법은 ResponseEntity를 이용하는 것이다.

응답 객체를 하나 만들어 준다.

@Getter
@AllArgsConstructor
public class RestApiException {

    private String errorMessage;
    private int statusCode;

}

 

그리고 Controller에서 예외를 직접 처리한다.

try {
    ...
    return new ResponseEntity<>(HttpStatus.OK);

} catch (IllegalArgumentException ex) {

    RestApiException exception =
        new RestApiException(ex.getMessage(), HttpStatus.BAD_REQUEST.value());

    return new ResponseEntity<>(exception, HttpStatus.BAD_REQUEST);
}

 

ResponseEntity는 다음 정보를 함께 반환할 수 있다.

  • HTTP Status
  • HTTP Header
  • HTTP Body

덕분에 API 응답을 자유롭게 구성할 수 있다.

 

 

하지만 Controller마다 try-catch를 작성하는 것은 상당히 비효율적이다.

모든 API마다 동일한 예외 처리 코드가 반복되기 때문에 결국 비즈니스 로직보다 예외 처리 코드가 더 많아질 수도 있기 때문이다.

@PostMapping(...)
public ResponseEntity<?> ...

@GetMapping(...)
public ResponseEntity<?> ...

@PutMapping(...)
public ResponseEntity<?> ...

 

 

Spring은 이를 해결하기 위해 @ExceptionHandler를 제공한다.

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<RestApiException> handleException(
        IllegalArgumentException ex) {

    RestApiException exception =
            new RestApiException(
                    ex.getMessage(),
                    HttpStatus.BAD_REQUEST.value());

    return new ResponseEntity<>(exception, HttpStatus.BAD_REQUEST);
}

이 메서드는 Controller 내부에서 IllegalArgumentException이 발생하면 자동으로 호출된다.

 

즉,

  • try-catch를 작성할 필요가 없다.
  • Controller의 비즈니스 로직이 훨씬 깔끔해진다.
  • 동일한 예외를 하나의 메서드에서 처리할 수 있다.

 

 

하지만 Controller가 여러 개라면 어떨까?

FolderController
ProductController
UserController
OrderController
...

모든 Controller에 동일한 @ExceptionHandler를 작성해야 한다.

결국 또 다른 중복이 발생한다.

 

 

Spring에서는 모든 Controller의 예외를 한 곳에서 처리할 수 있도록 @ControllerAdvice를 제공한다.

REST API에서는 @RestControllerAdvice를 사용하는 것이 일반적이다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<RestApiException> handleException(
            IllegalArgumentException ex) {

        RestApiException exception =
                new RestApiException(
                        ex.getMessage(),
                        HttpStatus.BAD_REQUEST.value());

        return new ResponseEntity<>(
                exception,
                HttpStatus.BAD_REQUEST);
    }

}

@RestControllerAdvice는

  • 모든 Controller에서 발생하는 예외를 처리하며
  • JSON 형태의 응답을 자동으로 반환한다.

따라서 Controller에서는 예외를 발생시키기만 하면 된다.

throw new IllegalArgumentException("중복된 폴더명입니다.");

이후의 응답 생성은 Global Exception Handler가 담당한다.

 

 

Global 예외 처리를 적용하면 다음과 같은 장점이 있다.

  • 예외 처리 코드 중복 제거
  • 모든 API가 동일한 응답 형식 유지
  • 유지보수성 향상
  • 새로운 예외 추가가 쉬움
  • 비즈니스 로직과 예외 처리의 관심사 분리

실무에서는 대부분 Global Exception Handler를 기본으로 사용한다.

 

 

예외 메시지를 코드에 직접 작성하면 유지보수가 어려워진다.

Spring에서는 messages.properties 파일을 이용해 메시지를 관리할 수 있다.

below.min.my.price=최저 희망가는 최소 {0}원 이상으로 설정해 주세요.
not.found.product=해당 상품이 존재하지 않습니다.

 

이후 MessageSource를 통해 메시지를 가져온다.

messageSource.getMessage(
    "below.min.my.price",
    new Integer[]{MIN_MY_PRICE},
    "Wrong Price",
    Locale.getDefault()
);

각 매개변수의 의미는 다음과 같다.

  • 첫 번째 : properties의 key
  • 두 번째 : 메시지 치환 변수
  • 세 번째 : 기본 메시지
  • 네 번째 : 언어(Locale)

이 방식을 사용하면 메시지를 한 곳에서 관리할 수 있으며, 다국어 지원(Localization)도 쉽게 구현할 수 있다.

 

 

Spring에서는 필요한 예외를 직접 정의해서 사용할 수도 있다. 이를 사용자 정의 Exception이라고 한다.

public class ProductNotFoundException extends RuntimeException {

    public ProductNotFoundException(String message) {
        super(message);
    }

}

 

서비스에서는 상황에 맞는 예외를 던진다.

throw new ProductNotFoundException(
    messageSource.getMessage(...)
);

 

Global Exception Handler에서는 해당 예외를 처리한다.

@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<RestApiException> handleException(
        ProductNotFoundException ex) {

    ...
}

이처럼 예외를 의미에 맞게 분리하면 코드의 가독성이 좋아지고, 어떤 상황에서 발생한 오류인지도 쉽게 파악할 수 있다.

 

 

정리하자면, 예외 처리는 단순히 오류를 막는 기능이 아니라 클라이언트와 서버가 정확하게 소통하기 위한 중요한 약속이다.

초기에는 try-catch와 ResponseEntity를 사용해 예외를 처리할 수 있지만, 프로젝트가 커질수록 코드 중복이 증가한다.

Spring에서는 @ExceptionHandler와 @RestControllerAdvice를 통해 예외 처리 로직을 중앙에서 관리할 수 있으며, MessageSource와 사용자 정의 Exception을 함께 활용하면 유지보수성과 확장성을 크게 높일 수 있다.

결국 좋은 예외 처리는 비즈니스 로직은 단순하게 유지하면서도, 일관된 API 응답을 제공하는 구조를 만드는 것이라고 할 수 있다.

 

 

📚  한줄 정리

  1. 단위 테스트는 작은 기능을 독립적으로 검증하는 테스트이며, Mockito를 활용하면 외부 의존성을 제거하고 Service 로직만 안정적으로 테스트할 수 있다.
  2. 통합 테스트는 여러 컴포넌트가 함께 동작하는 과정을 검증하는 테스트이며, @SpringBootTest를 통해 실제 Spring 컨테이너를 실행하여 통합 테스트를 수행할 수 있다.
  3. Spring AOP는 핵심 비즈니스 로직과 공통 기능을 분리하여 코드의 중복을 줄이고 유지보수성을 높이는 기술이다.
  4. 예외 처리는 애플리케이션 실행 중 발생하는 오류를 적절하게 처리하여 시스템의 안정성을 유지하고, 클라이언트에 일관된 응답을 제공하는 과정이다.

'Bootcamp > Fundamentals' 카테고리의 다른 글

MSA와 Spring Cloud  (0) 2026.06.30
MSA 요약  (0) 2026.06.29
Spring 숙련 - 2  (0) 2026.06.25
Spring 숙련 - 1  (0) 2026.06.24
Spring 입문 - 2  (0) 2026.06.23