💻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
- 개인 과제 시작하기