- 본 서비스는 사용자들이 개인 재무를 관리하고 지출을 추적하는 데 도움을 주는 애플리케이션입니다.
- 이 앱은 사용자들이 예산을 설정하고 지출을 모니터링하며 재무 목표를 달성하는 데 도움이 됩니다.
- 유저는 본 사이트에 들어와 회원가입을 통해 서비스를 이용합니다
- 예산 설정 및 설계 서비스
월별
총 예산을 설정합니다.- 본 서비스는
카테고리
별 예산을 설계(=추천)하여 사용자의 과다 지출을 방지합니다.
- 지출 기록
- 사용자는
지출
을금액
,카테고리
등을 지정하여 등록 합니다. - 언제든지 수정 및 삭제 할 수 있습니다.
- 사용자는
- 지출 컨설팅
월별
설정한 예산을 기준으로 오늘 소비 가능한지출
을카테고리
별로 알려줍니다.
- 매일 발생한
지출
을카테고리
별로 안내받습니다.
-
NAT는 Application에서 나가는 요청을 담당하고 있습니다.
- 나가는 요청을 따로 표시하면 혼잡해지기 때문에 요청에 대한 처리만 표시하였습니다.
-
Nginx 설정 ( Load Balance, Certbot )
docker run --name expense-manager-db -e MYSQL_DATABASE=mysql-db -e MYSQL_USER=expense -e MYSQL_PASSWORD=manager -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql
docker run --name expense-manager-redis -d -p 6379:6379 redis
./script/build.sh
- 스크립트 실행시 실행되고 있는
expense-manager
를 종료합니다. - 실행되고 있는
expense-manager
를 종료 후에 테스트를 진행하기 위해 도커로33306
,43306
포트로MySQL
이 실행되고,36379
포트로Redis
가 실행됩니다. - 빌드 후에 테스트용으로 사용한 도커를 종료하고
container
를 제거합니다. container
제거 후에 빌드된.jar
를 실행합니다.
- 위 스크립트 실행후 http://localhost:8080/index.html 에서 볼 수 있습니다.
- 속도 개선을 테스트 하기 위해 유저를 500명, 지출 데이터를 1000만건 입력 후 성능 테스트 진행하였습니다.
- 쿼리에서
Budget
이MemberCategoryId
의FK Index
만 적용되어 느렸던 쿼리를 검색조건인expenseAt
과 함께복합 인덱스
로 성능 개선 하였습니다.- 1200% 성능 개선 ( 2s 191ms --> 164ms )
- 개선 전 ( 2s 191ms )
- Read/Write 구분
- 테스트를 위해 매번 테스트에서 데이터를 준비하는 과정을 생략할 수 있습니다.
- Read Database 분리
- 데이터 베이스 준비를 반복해서 하지 않기 위해서 Read Database를 분리 했습니다.
- Write Data Base 기본 구성
- 테스트에서
@Transactional
을 사용하면 테스트 전체에 트랜잭션이 걸려서 실제 동작과 다르게 작동할 수 있기 때문에 Write Database를 분리 했습니다.
- 테스트에서
- 반복되는 테스트의 실행시간을 줄이려고 노력하였습니다.
- 테스트 병렬 실행으로 테스트 실행 시간이 줄어들어 빌드 시간이 개선 되었습니다.
- 145% 속도 개선 ( 29s --> 20s )
- 개선 전
- await
- 비동기적으로 일어나는 일을 지속적으로 상태 확인 후 상태가 정상적으로 변한다면 테스트에 성공하게 됩니다.
- Redis의 TTL 테스트에 적용하였습니다.
- WebClient
- 비동기적으로 webhook을 보내게 되는데 보내는 주소를 MockServer로 적용하여 MockServer에서 요청을 받는다면 성공합니다.
- Discord Event Linstener Test에 적용하였습니다.
@WebMvcTestWithoutSecurityConfig
- Security가 필요 없는 테스트(ex: 회원가입 등)에서는
SpringSecurity
의 기본 설정로드를 막습니다.
- Security가 필요 없는 테스트(ex: 회원가입 등)에서는
@WithMemberPrincipals
- 어노테이션을 적용한 테스트에서는
WithMemberPrincipalsSecurityContextFactory
에서SecurityContextHolder
에MemberDetails
가 포함된 가짜Authentication
을 넣어줍니다.
- 어노테이션을 적용한 테스트에서는
TestSecurityConfig
MemberDetails
를 적용하였기 때문에 테스트에서SecurityContextHolder
에Authentication
이 있어야 합니다.TestSecurityConfig
를 적용한 테스트에@WithMemberPrincipals
를 적용하여 테스트 하였습니다.
Mockito
를 사용하여 mock 테스트를 진행할때mock의 입력값
에 대해서도 검증 할 수 있습니다.- 입력값을 검증하지 않는다면 입력값이 다를 경우에도 테스트가 성공하기 때문에 반쪽짜리 테스트가 됩니다.
id
와password
로 인증을 정상적으로 완료하면액세스 토큰
을Authorization 헤더
로,리프레시 토큰
은쿠키
로 응답해줍니다.
Authorization 헤더
에 들어오는액세스 토큰
을 해석하여 정상적으로 해석되면 목표url
에 접근할 수 있습니다.- Authorization 헤더에 들어온 토큰이
정상적이이 않은 경우
토큰이 정상적이지 않다는 응답을 내려줍니다.
- 위의 두 인증과정에서 각각
JwtAuthenticationFilter
에서는조회된 유저
로,JwtVerificationFilter
에서는토큰에 있는 인증정보
로 각각 인증된 유저 객체를 생성합니다. - 인증된 유저는
@AuthenticationPrincipal
어노테이션이 있으면Controller
에서ArgumentResolver
를 통해MemberDetails
타입으로 받을 수 있습니다. - 인증된 유저 타입을
MemberDetails
로통일
시킴으로써 컨트롤러에서 인증된 유저라면MemberDetails
타입으로부담없이
꺼내 쓸 수 있습니다.
SecurityConfig
에서 각 URL에 대해서 필요한 권한을 설정합니다.- 위에서 인증된 권한이 접근 가능한 권한이라면 목표
url
에 접근할 수 있습니다.
- Google Java Format
- 장점:
- 협업을 한다고 가정했을 때, 다른사람의 코드 포맷에 대해서 이야기할필요가 없다.
- 횡스크롤이 없어서 코드 볼때 편하다.
- 린트를 적용함으로서 코드 포매팅이 되지 않았다면 빌드가 되지 않는다.
- 단점:
- lambda 코드에서 엔터가 자주 발생한다.
- 린트를 적용함으로서 코드 포매팅이 되지 않았다면 빌드가 되지 않는다.
- 장점: