어제는 개인 사정으로 인해 자리를 오래 비워야 했고, 바로 그 전 날에 프로젝트가 막 마무리 된 참이라서 쉬고 싶었기에 그냥 부트캠프에 참여를 안하고, 휴식을 취했다. 따라서 35일차 후기는 없다.
오늘 CodeKata는 2문제를 풀었다. 첫 번쨰 문제를 풀 당시 BFS로 풀었다가 틀려보고 나서, Dijkstra로 고쳐서 정답이란 결과를 얻었다. Kotlin으로 Dijkstra를 처음 적용한 것도 있어서 여기서 시간을 많이 소모했다. 두 번째 문제는 Queue를 활용하는 문제 같은데, 어차피 Queue의 사이즈가 중요한 것 같아서, Int 변수 하나로 대체가 가능했고, 덕분에 여기서는 시간을 단축해서 총 2문제를 풀었다.
Spring 심화 강의를 뒤늦게 보고 시작했다. 일단 절반 가량의 강의를 들었고, 배운 것은 많지만, 각 주제에 관해 한 두개 정도의 개념을 정리하고자 한다.
일단 AOP에 대해 이야기해보자, AOP는 Aspect Oriented Programming의 준말로 부가기능을 모듈화해서 전체에 걸쳐 재사용할 수 있도록 하는 프로그래밍이다. 이 기법은 보통 try~catch와 같은 예외처리나 로깅, 트랜잭션 등을 활용할 때, 주로 사용한다.
AOP의 주요 개념은 Advice, Pointcut, JoinPoint 등으로 나뉘어진다.
advice는 부가 로직을 의미한다. 즉, 어떠한 일을 하는 지를 나타낸다. pointcut은 부가 로직을 적용할 위치이다. advice와 joinPoint를 합쳐서 Aspect란 단위를 사용한다. JoinPoint는 Aspect가 적용 될 수 있는 위치이다. method, Class의 생성자가 호출되는 시점, exception 발생 시점등이 있다.
내가 이 AOP를 배우면서 가장 기억하고 싶은 것은 애노테이션을 활용한 AOP이다.
Kotlin에서 Annotation을 정의할 때, 추가 속성을 달아 줄 수 있다.
@Target은 Annotation을 대상이다. 대상이 Class가 될 수도 있고, Method가 될 수도 있고, 변수가 될 수가 될 수도 있다.
그럴 때엔 AnnotationTarget enum을 활용한다. @Retention은 Annotation 어느 시점까지의 사용여부이다. 보통 RUNTIME으로 설정하므로 굳이 건들일 필요는 없을 것 같다. 밑은 강의 자료의 코드를 가져왔다.
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
// 함수 대상 런타임 애노테이션
annotation class StopWatch()
이렇게 Annotation을 정의하고
@Aspect
@Component
class StopWatchAspect {
private val logger = LoggerFactory.getLogger("Execution Time Logger")
@Around("@annotation(com.teamsparta.courseregistration.infra.aop.StopWatch)")
fun run(joinPoint: ProceedingJoinPoint) {
val stopWatch = StopWatch()
stopWatch.start()
joinPoint.proceed()
stopWatch.stop()
val timeElapsedMs = stopWatch.totalTimeMillis
val methodName = joinPoint.signature.name
val methodArguments = joinPoint.args
logger.info("Method Name: $methodName | Arguments: ${methodArguments.joinToString(", ")} | Execution Time: ${timeElapsedMs}ms")
}
}
이렇게 AOP를 적용하면 @StopWatch를 단 Method의 시간을 잴 수 있다.
잘 사용하면 굉장히 편리한 기능일 것 같으니 잘 기억해두자.
이 다음엔 Spring Security에 관해 배웠다. 사실 내가 전에 해던 프로젝트에서 적용한 Spring Security는 구글링을 해서 어느정도 짜집기 해서 만든 코드라서 완전히 이해하지 못했는데, 이번 강의를 들으면서 전부는 아니지만 어느정도 이해하는 데 도움이 된 것 같다. 그 중 가장 기억에 남는 것은 예외 처리와 관련 된 코드이다.
인증이 되었지만, 권한이 없을 때에는 403 StatusCode를 사용하는데, Spring Security에서는 인증이 되지 않더라도 403 Status Code를 사용한다. 이것을 처리하기 위해서는 인증이 되지 않았을 때에 사용하는 객체인 AuthenticationEntryPoint를 상속받아서 commence 메소드를 override를 해서 SecurityFilter에 추가해야한다.
밑의 코드는 강의자료 수록된 코드이다.
@Component
class CustomAuthenticationEntrypoint: AuthenticationEntryPoint {
override fun commence(
request: HttpServletRequest,
response: HttpServletResponse,
authException: AuthenticationException
) {
response.status = HttpServletResponse.SC_UNAUTHORIZED
response.contentType = MediaType.APPLICATION_JSON_VALUE
response.characterEncoding = "UTF-8"
val objectMapper = ObjectMapper()
val jsonString = objectMapper.writeValueAsString(ErrorResponse("JWT verification failed"))
response.writer.write(jsonString)
}
}
여기서 재정의를 하고 이 객체를 Filter에 추가하면 끝이다.
@Configuration
@EnableWebSecurity
class SecurityConfig(
private val jwtAuthenticationFilter: JwtAuthenticationFilter,
private val authenticationEntrypoint: AuthenticationEntryPoint
) {
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
return http
.httpBasic { it.disable() }
.formLogin { it.disable() }
.csrf { it.disable() }
.authorizeHttpRequests {
it.requestMatchers(
"/login",
"/signup",
"/swagger-ui/**",
"/v3/api-docs/**"
).permitAll()
.anyRequest().authenticated()
}
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
// 예외처리
.exceptionHandling{
it.authenticationEntryPoint(authenticationEntrypoint)
}
.build()
}
}
이전에는 예외처리에 다소 무신경했는데, 앞으로는 신경을 써보고 싶다는 생각이 들었다.
오늘은 여기까지, 내일은 마저 QueryDSL 강의와 테스트에 관한 강의를 듣고, 개인 과제에 들어가자, 사실 이번 개인 과제는 이전에 했던 코드를 그냥 제출해도 큰 문제는 없어보이지만, 그래도 그대로 제출하면 성의가 없어보이니 수정을 거쳐야겠다. 위에서의 예외처리를 추가하는 것도 괜찮은 생각인 것 같다.
'부트캠프 일지' 카테고리의 다른 글
| 부트캠프 39일차 후기 (1) | 2024.01.22 |
|---|---|
| 부트캠프 38일차 후기 (0) | 2024.01.19 |
| 부트캠프 34일차 후기 (1) | 2024.01.15 |
| 뉴스피드 프로젝트 회고 (0) | 2024.01.15 |
| 부트캠프 33일차 후기 (1) | 2024.01.12 |