본문 바로가기

전체 글373

Spring Cache(스프링 캐시) 적용 NOW 프로젝트를 진행하면서 기록한 글입니다. Cache를 적용하게 된 이유 Cache를 적용하기 전, 수백 명이 동시에 메인 페이지로 접속할 경우에 대해서 생각해보았습니다. 수백 명의 사용자는 게시물 목록을 가져오는 요청을 보낼 것이고, 아래 핸들러 메서드가 수백 개의 쓰레드(Thread)에 의해 실행될 것입니다. 이로 인해 트래픽이 급격하게 증가하게 되며, 이는 주로 WAS(Web Application Server)와 데이터베이스(DB) 간의 통신량이 급증하는 것을 의미합니다. (아래는 모든 게시물 목록을 가져올때 실행되는 쿼리입니다.) 이에 따라, 우선적으로 현재 상황에서 부하를 최소화 하기 위한 방법으로 쿼리 최적화보다는 자주 요청되는 쿼리 결과를 Cahce하여 서버 응답 성능을 향상시키고자 했습.. 2023. 8. 26.
격리된 테스트 환경 구성 후 Repository 객체에 대한 테스트 코드 NOW 프로젝트를 진행하면서 기록한 글입니다. 격리된(Isolated) Repository 테스트 모든 비즈니스 로직(도메인, 서비스 레이어)에 대한 테스트를 위해 Mock 라이브러리(Mockito)를 활용했었습니다. 그러나 이러한 방식만으로는 실제 애플리케이션을 사용하는 과정에서 나타나는 일부 문제를 잡아내기 어려웠던 상황이 종종 발생했었습니다. 또한, 매번 UI에 의존하여 데이터가 올바르게 입력되고 표시되는지를 확인하는 것도 어려웠습니다. 이러한 상황이 발생했던 이유는 Repository 레이어에 대한 테스트를 스킵했기 때문인데요. 이를 해결하고자, 격리된 Repository 테스트 환경을 구성하여 테스트를 진행하기로 했습니다. 추가적으로, 격리된 환경으로 구성해야했던 이유는 각 테스트가 독립적으로 .. 2023. 8. 22.
파사드 패턴을 활용한 서비스 의존성 주입(with 트랜잭션 설정) NOW 프로젝트를 진행하면서 기록한 글입니다. 파사드 패턴(Facade Pattern)을 적용하게 된 이유 컨트롤러(Controller)에서 다음과 같은 문제점을 발견했는데요. 다수의 서비스 주입: 다수의 서비스 객체를 의존하고 있어서, 서비스 객체들과의 결합도가 높은 상태 컨트롤러의 복잡한 역할: 단순히 HTTP 요청과 응답 처리를 넘어서서 서비스 로직까지 직접 처리 제가 왜 그렇게 생각했는지 실제 구현된 코드를 살펴보겠습니다. deletePhoto 핸들러 메서드는 다음과 같이 실행됩니다. 게시글 삭제 권한 확인(비즈니스 로직) 모든 댓글 삭제 모든 첨부파일 삭제 게시글 삭제 해당 코드를 볼때마다 나쁜 냄새(Bad Smell) 가 난다는 것을 계속 느꼈으며, 리팩토링이 필요하다고 생각했습니다. 처음으로.. 2023. 8. 22.
Spring의 Argument Resolver를 활용한 principal 주입 NOW 프로젝트를 진행하면서 기록한 글입니다. 기존: 토큰의 클레임 값을 추출하여 요청의 속성으로 설정 현재 프로젝트에서는 토큰 기반 인증방식을 선택해서 사용하고 있습니다. 이전 포스팅에서 소개해드렸습니다. 사용자가 로그인 후에 게시글을 작성/수정/삭제 할 때, 컨트롤러 진입 전 토큰을 확인합니다. 유효한 토큰이라면, 다음과 같이 클레임 값을 추출하여 HttpServletRequest 객체 속성을 추가했는데요. 정상적으로 HttpServletRequest 객체 속성을 추가되었다면, 컨트롤러 핸들러 메서드에서는 @RequestAttribute 어노테이션을 사용해서 받아올 수 있었습니다. 물론, 인터셉터를 통해 설정된 속성을 받아오는 것이 틀린방법은 아니지만.. 코드를 볼 때마다 무언가.. 나쁜 냄새(Bad.. 2023. 8. 14.
Spring AOP로 Slack 알람 구현 NOW 프로젝트를 진행하면서 기록한 글입니다. 해당 글은 Slack 웹훅 설정 및 의존성 추가에 대해서 설명하지 않습니다. Slack 알람 기능을 구현하게 된 계기 현재 NOW 프로젝트는 일정기간 테스트를 한 후 정식으로 서비스할 계획이 있습니다. 그러므로, 안정적이고 효율적인 서비스 제공을 위해 서비스에 오류가 발생했을 때 다음과 같은 대응방안을 생각했습니다. 신속한 상황 공유 정확한 문제 식별과 추적 따라서, 다음과 같이 internal server error 발생 시에 Slack으로 알림을 보내주도록 설정하고자 합니다. Global Exception Advice의 Exception을 처리하는 핸들러 메서드 그렇다면 Slack으로 알람을 보내도록하는 핸들러 메서드는 어떻게 정의해야할까요? 지난 포스팅.. 2023. 8. 13.
Spring AOP로 모든 요청과 응답 로그 기록 NOW 프로젝트를 진행하면서 기록한 글입니다. 디버깅을 위한 로깅(logging) 프로젝트를 진행하다보니, 애플리케이션이 정상적으로 동작하는지 확인하기 위한 목적 혹은 문제가 발생했을 때 원인을 파악하기 위한 디버깅을 목적으로 메시지를 출력했습니다. 이때 사용했던 라이브러리는 Lombok이며, @Slf4j 어노테이션을 사용했습니다. Contoller 핸들러 메서드에서 공통으로 등장하는 로깅 코드 아래와 같이 모든 Controller 핸들러 메서드에서는 무조건 로깅하는 코드가 포함되어야 했습니다. 이러한 상황이 발생하다보니 다음과 같은 문제점을 발견했습니다. 로깅 코드 반복 핵심 로직의 가독성 저하 그렇다면, 전에 문서만 봐두고 써보진 않은 Spring AOP를 통해 이러한 문제를 해결할 수 있지 않을까?.. 2023. 8. 10.
토큰 기반 인증을 선택한 이유 NOW 프로젝트를 진행하면서 기록한 글입니다. 기존 세션 기반 인증 이전 포스팅에서는 HTTP Session을 직접 구현해 해보면서, 몇가지의 보안, 확장성, 성능의 문제가 있다고 생각했습니다. 간략하게 다시 적어보자면, 보안 측면 세션 하이재킹 (Session Hijacking): 세션 ID를 도용하여 피해자의 세션에 접근하는 공격 세션 고정 공격 (Session Fixation): 세션 ID를 강제로 주입한 후, 세션 ID로 인증하는 공격 확장 측면 세션 불일치: 서버의 확장으로, 세션 정보를 공유하기 위한 Sticky Session, Session Clustering, 세션 스토리지 외부 분리 등의 작업 요구 따라서, 현재 프로젝트에서는 세션 기반 인증 방식이 아닌 토큰 기반 인증 방식을 선택하게 .. 2023. 8. 8.
상속을 활용한 Global Exception Handler 리팩토링 NOW 프로젝트를 진행하면서 기록한 글입니다. Global Exception Handler SpringBoot 환경에서의 사용자 정의 예외 처리 이전 포스팅에서는 기존 Controller에서 Exception을 처리하던 로직을 Global Exception Handler로 위임 후 ErrorResponse 객체를 생성해 응답했습니다. 하지만, 예외 클래스가 많아질수록 GlobalExceptionHandler에서 다음과 같은 문제가 발생했습니다. 코드 중복 발생 각 예외 클래스에서 코드를 중복하여 구현하면서 예외 처리 로직이 반복되어 작성 가독성 저하 예외를 처리하는 핸들러 메소드들이 여러 곳에 흩어져 있으면 가독성이 떨어지며 로직 파악이 어려움 확장성 제한 새로운 종류의 예외가 필요할 때마다 관련 핸들러.. 2023. 7. 20.
빈번하게 변경되지 않는 게시글 카테고리를 Enum으로 관리 NOW 프로젝트를 진행하면서 기록한 글입니다. 게시글의 카테고리를 DB에서 관리하다 하나의 게시글을 DB에 저장한다고 하면, 해당 글이 공지 글인지, 커뮤니티 글인지, 사진 글인지, 문의 글인지 구분해야합니다. 게시글 테이블의 카테고리 컬럼을 추가해서 해결할 수도 있겠지만, 게시글 카테고리에 한정하지 않고, 서비스 내에서 사용할 카테고리성 데이터에도 사용할 수 있도록 테이블을 설계해야 한다고 생각했습니다. 다음과 같은 용도로요. 출처: 우아한기술블로그(https://techblog.woowahan.com/2527/) 따라서, 현재 초기 프로젝트의 테이블 구조입니다. 게시글 카테고리에 사용할 데이터를 코드 그룹(tb_code_group)과 하위 코드 테이블(tb_sub_code)에서 다음과 같이 관리했습니.. 2023. 7. 18.