[TIL] 백엔드 부트캠프 9주차 (2024/09/10 화) AOP / 테스트코드

2024. 9. 10. 23:24·TIL 🔖/TIL

💻Today's Schedule

09:00 ~ 10:00 CODEKATA
10:00 ~ 11:00 개인 학습
11:00 ~ 12:00 팀미팅/코드리뷰
12:00 ~ 13:00 점심 식사
13:00 ~ 16:00 개인 학습
16:00 ~ 18:00 베이직 특강 8회차
18:00 ~ 19:00 저녁 식사
19:00 ~ 21:00 개인 학습

✍ Today I Learned

CODEKATA

  • 알고리즘 (61번 로또의 최고 순위와 최저 순위)
 

[프로그래머스/JAVA] 로또의 최고 순위와 최저 순위

문제 설명 로또 6/45(이하 '로또'로 표기)는 1부터 45까지의 숫자 중 6개를 찍어서 맞히는 대표적인 복권입니다. 아래는 로또의 순위를 정하는 방식입니다.순위당첨 내용16개 번호가 모두 일치25개

fargoewave.tistory.com


AOP 개념과 실습 정리

1. AOP와 OOP의 차이

OOP(Object-Oriented Programming)는 객체 중심의 프로그래밍을 지향하며, 코드의 복잡성을 객체로 분리해 해결하는 방식이다. 반면 AOP(Aspect-Oriented Programming)는 관점 지향 프로그래밍으로, 프로그램을 개발하면서 발생하는 중복적이고 부가적인 로직들을 분리해 보다 관리하기 쉽도록 하는 것을 목표로 한다.

AOP는 여러 곳에서 반복되는 관심사를 하나의 모듈로 추출해 관리할 수 있도록 해준다.

 

2. AOP가 필요한 이유

어떤 기능이 코드 곳곳에 중복되어 있을 때 이를 일일이 고치는 건 비효율적이다. 예를 들어 메서드 실행 시간을 측정하는 코드를 여러 곳에 넣어야 한다면, 수정 사항이 생길 때마다 해당 코드들을 모두 변경해야 한다. AOP를 사용하면 중복된 코드를 분리해 유지보수성을 높이고 코드의 가독성을 유지할 수 있다.

3. AOP 용어 정리

  • 핵심기능과 부가기능
    • 핵심기능: 메서드의 주요 로직, 예를 들어 "수업을 생성하는 기능".
    • 부가기능: 메서드의 본래 기능 외에 추가적으로 필요한 로직, 예를 들어 "메서드 실행 시간 측정".
    • 횡단관심사: 여러 곳에서 공통적으로 필요한 부가기능들을 하나로 묶어 관리하는 개념.
  • AOP 용어 설명
    • 어드바이스(Advice): 실제 실행되는 부가기능 코드.
    • 포인트컷(Pointcut): 부가기능이 적용될 특정 메서드나 클래스의 범위를 지정하는 규칙.
    • 타겟(Target): 어드바이스가 적용되는 객체 또는 메서드.
    • 조인포인트(JoinPoint): 어드바이스가 실행되는 구체적인 시점, 일반적으로 메서드 호출 순간.
    • 애스팩트(Aspect): 어드바이스와 포인트컷을 묶어 하나의 모듈로 구성한 것.

4. AOP 적용 실습

  • 어드바이스 구현
    • Aspect 클래스에서 어드바이스를 구현한다. 예를 들어 @Before 어노테이션을 사용해 메서드 실행 전에 특정 로직을 수행할 수 있다.
    • @AfterReturning, @AfterThrowing, @After 등 여러 종류의 어드바이스를 통해 메서드 실행 후나 예외 발생 시에도 추가적인 로직을 적용할 수 있다.
@Before("serviceLayer()")
public void beforeMethod() {
    log.info("메서드 실행 전에 호출");
}
  • 포인트컷 구현
    • 포인트컷은 어드바이스를 적용할 구체적인 범위를 지정한다. 예를 들어 특정 패키지 내 모든 메서드에 적용할 수 있다.
@Pointcut("execution(* com.example.service..*(..))")
private void serviceLayer() {}
  • 부가기능 시간 측정
    • @Around 어드바이스를 사용하면 메서드 실행 전후로 시간을 측정할 수 있다.
@Around("serviceLayer()")
public Object advicePackage(ProceedingJoinPoint joinPoint) throws Throwable {
    long startTime = System.currentTimeMillis();
    try {
        return joinPoint.proceed();
    } finally {
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        log.info("ExecutionTime: {}ms", executionTime);
    }
}
  • 어노테이션 기반 포인트컷
    • 직접 만든 어노테이션을 활용하여 특정 메서드에만 부가기능을 적용할 수도 있다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {}

 

이 어노테이션이 붙은 메서드에만 어드바이스가 동작하도록 포인트컷을 설정할 수 있다.

@Pointcut("@annotation(com.example.TrackExecutionTime)")
private void trackExecutionTime() {}

 

5. AOP의 동작 원리

스프링은 빈을 등록할 때 '빈 후처리기'를 거친다. 이 과정에서 AOP가 적용된 빈은 프록시 객체로 감싸지고, 해당 빈은 프록시 객체로 스프링 컨테이너에 등록된다. 메서드가 호출될 때 실제 메서드가 아닌 프록시 객체가 호출되어 부가기능을 먼저 실행한 후, 본래의 로직을 수행하게 된다.

 

프록시 객체가 생성되었는지 확인하려면 다음과 같은 코드를 사용해 확인할 수 있다.

public void verifyProxyBean() {
    Object bean = applicationContext.getBean("courseService");
    log.info(bean.getClass().getName());
}

 

SpringBoot Test 개념과 실습

1. 테스트의 중요성
코드 품질을 유지하기 위해 테스트는 선택이 아닌 필수다. 이를 해 JUnit5, Mock, Unit Test, WebMvc Test, SpringBoot Test 등을 활용하여 테스트 코드를 작성하고, 테스트 커버리지를 높이는 방법을 익힌다. 일반적으로 테스트는 Given-When-Then 패턴을 따른다.

  • JUnit5
    • JUnit5는 자바에서 사용하는 단위 테스트 프레임워크다.
    • JUnit5는 JUnit Platform, JUnit Jupiter, JUnit Vintage로 구성되어 있다.
    • 주요 어노테이션: @BeforeAll, @BeforeEach, @Test, @AfterEach, @AfterAll.
  • JUnit5 의존성 추가
dependencies {
    testImplementation(platform("org.junit:junit-bom:5.9.1"))
    testImplementation("org.junit.jupiter:junit-jupiter")
}
dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
  • 간단한 JUnit 테스트 테스트 / 결과 확인은 Assertions를 사용해 검증한다.
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class BasicTest {
    @Test
    @DisplayName("더하기 테스트")
    public void calTest() {
        // given
        int a = 1;
        int b = 3;

        // when
        int sum = a + b;

        // then
        assertEquals(4, sum);
    }
}

 

2. Mocking과 Unit Test

  • Mock의 개념
    • Mock은 실제 객체 대신 가짜 객체를 만들어 테스트하는 방식이다.
    • Mockito 프레임워크는 Mock 객체를 쉽게 생성하고, 관리하며 검증할 수 있도록 해준다.
    • Mock 기반 테스트는 빠르며 외부 시스템에 의존하지 않는다.
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class MovieServiceTest {
    @Mock
    MovieRepository movieRepository;

    @InjectMocks
    MovieService movieService;

    @Test
    @DisplayName("영화 단건조회 테스트")
    public void getMovieTest() {
        // given
        long movieId = 1L;
        Movie movie = new Movie("Movie Title");
        given(movieRepository.findById(anyLong())).willReturn(Optional.of(movie));

        // when
        MovieResponse result = movieService.getMovie(movieId);

        // then
        assertNotNull(result);
    }
}
  • Mock 어노테이션 설명
  • @Mock: 테스트에서 사용할 가짜 객체를 선언한다.
  • @InjectMocks: 가짜 객체를 주입하여 실제 객체의 의존성을 설정한다.

3. Unit Test 원칙

  • FIRST 원칙에 따라 Unit Test는 다음과 같이 작성되어야 한다:
    • Fast: 테스트는 빨라야 한다.
    • Isolated: 각 테스트는 독립적으로 실행되어야 한다.
    • Repeatable: 환경에 상관없이 실행할 때마다 같은 결과를 만들어야 한다.
    • Self-validating: 테스트는 성공/실패 여부가 명확해야 한다.
    • Timely: 개발 단계에서 바로 테스트를 작성해야 한다.

4. WebMvc Test

  • WebMvcTest 어노테이션
    • @WebMvcTest는 컨트롤러 테스트를 위한 어노테이션으로, 전체 애플리케이션이 아닌 웹 계층만을 테스트한다.
@WebMvcTest(MovieController.class)
public class MovieControllerTest {
    @MockBean
    private MovieService movieService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void 영화_단건_조회() throws Exception {
        // given
        long movieId = 1L;
        given(movieService.getMovie(anyLong())).willReturn(new MovieResponse(1L, "영화 제목", 1992));

        // when
        ResultActions result = mockMvc.perform(get("/api/v1/movies/{movieId}", movieId));

        // then
        result.andExpect(status().isOk());
    }
}
  • @MockBean: 스프링 컨텍스트에서 관리되는 빈을 mock 객체로 교체한다.
  • MockMvc: HTTP 요청을 시뮬레이션하여 컨트롤러 테스트를 진행한다.

5. SpringBoot Test

@SpringBootTest는 애플리케이션 전체를 테스트하는 통합 테스트 환경을 제공한다. 실제 애플리케이션처럼 빈을 로드하고 환경을 구성하여 테스트한다.

@SpringBootTest
public class MovieServiceTest {
    @Autowired
    MovieService movieService;

    @Test
    @DisplayName("영화 단건조회 테스트")
    public void getMovieTest() {
        // given
        int movieId = 1;

        // when
        MovieResponse movie = movieService.getMovie(movieId);

        // then
        assertNotNull(movie);
    }
}

 

6. Test Coverage

테스트 커버리지는 애플리케이션의 테스트가 얼마나 충족되었는지를 나타내는 지표다. IntelliJ에서는 테스트 코드 실행 시 Run with Coverage를 통해 커버리지 확인이 가능하다. jacoco, sonarqube, jenkins 등을 활용하여 CI/CD 파이프라인에서 커버리지를 확인할 수 있다. 커버리지가 높다고 해서 항상 코드가 완벽한 것은 아니며, 테스트 품질이 중요하다.


📝 회고

이전 프로젝트에서 '테스트 코드 짜셨어요?'라는 질문을 너무 많이 들었었는데
사실 테스트 코드를 학습할 시간도 여유도 없었기 때문에 이 부분이 너무 궁금했다. 

오늘 특강때 완전! 완전! 개념을 다 이해한 느낌을 받았는데 
내배캠을 참여하며 내용을 한번에 이해한게 처음이었던 것 같아 너무 즐겁게 강의를 수강했다. 
 
당장 코드에 적용할 수 있을지는 모르겠지만~ 또 하다보면 하겠지~ 

 


🔖 Tomorrow's Goal

  • 개인 과제 시작하기
'TIL 🔖/TIL' 카테고리의 다른 글
  • [TIL] 백엔드 부트캠프 9주차 (2024/09/12 목) 레거시 코드의 테스트 코드 기반 리팩토링 -2
  • [TIL] 백엔드 부트캠프 9주차 (2024/09/11 수) 레거시 코드의 테스트 코드 기반 리팩토링
  • [TIL] 백엔드 부트캠프 9주차 (2024/09/09 월) 카카오 로그인 / 테스트
  • [TIL] 백엔드 부트캠프 8주차 (2024/09/06 금) 뉴스피드 프로젝트 마무리
fargoe
fargoe
    fargoe
    fargoewave
    fargoe
    GitHub
    전체
    오늘
    어제
    • 분류 전체보기 (166)
      • TIL 🔖 (140)
        • TIL (69)
        • 코딩테스트 (71)
      • DEV (14)
        • Java & Spring (7)
        • MySQL (3)
        • Git&Github (4)
      • 개발지식 (10)
        • 알고리즘 (2)
        • 자료구조 (8)
        • CS (0)
      • 3D (1)
        • Unity (1)
      • ETC (0)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
fargoe
[TIL] 백엔드 부트캠프 9주차 (2024/09/10 화) AOP / 테스트코드
상단으로

티스토리툴바