[SpringBoot] ThreadLocal을 활용한 인증관리

개요 Spring Boot는 스레드 풀을 통해 요청에 맞는 스레드를 할당합니다. 인증된 사용자의 ID를 ThreadLocal에 저장하여 전역적으로 접근 가능하게 하려고 했습니다. 문제점 ThreadLocal에 저장되고 초기화 하지 않았을 때 다음 사용자가 인증에 실패해도 전에 사용자의 정보가 남아있어 비즈니스 로직이 실행되는 문제가 발생했습니다. 시나리오 사용자 1이 ID “1"로 인증 성공 후 ThreadLocal에 저장 사용자 2가 토큰 없이 요청 (인증 실패) 사용자 2가 사용자 1의 정보로 비즈니스 로직 실행 해결방안 ...

June 20, 2024 · Lee WooJin

[SpringBoot] In-Memory 환경 Memory Leak

InMemory 인증번호 관리 회원가입 시 휴대폰 번호로 인증번호를 전송하고 인증번호를 통해 회원을 검증하는 방식을 구현했습니다. 인증번호 발행 랜덤 번호를 생성하여 휴대폰으로 전송하고 Map에 번호를 저장합니다. 인증번호 검증 현재 인증시간의 유효성을 확인하고 인증번호를 검증합니다. 일치하면 Map에서 key를 제거하지만, 불일치 시 메모리에 계속 남아있게 됩니다. 메모리 누수 문제 key를 요청하고 검증을 하지 않았을 때 계속해서 데이터가 Map에 참조하고 있어 GC가 메모리를 수거하지 않습니다. 문제 시나리오 사용자가 인증번호 요청 Map에 {phoneNumber: authCode} 저장 사용자가 인증을 완료하지 않음 Map에 데이터가 계속 남아있음 반복 시 메모리 누수 발생 해결방법 ...

June 20, 2024 · Lee WooJin

BeanPay Detail 삭제 이유

변경 전 테이블 구조 기존에는 BeanPayDetail과 PaymentDetail이 분리되어 있었습니다. BeanPayDetail 역할 BeanPayDetail은 기존의 빈페이의 변경 내역에 관한 정보를 담고 있었습니다. 4가지 상태값으로 구분되었습니다: 충전 지출 입금 출금 PaymentDetail 역할 PaymentDetail은 세부 결제 내역 정보를 저장하며 PaymentStatus로 상태를 관리합니다: 결제 환불 중복 문제 발견 PaymentDetail과 BeanPayDetail의 ProcessStatus 값이 중복 사용되고 있었습니다. 변경 이유 PaymentDetail과 BeanPayDetail의 ProcessStatus 값이 중복 사용됨 BeanPayDetail의 추가 행 생성 제거 가능 로직이 하나의 Depth가 줄어들고 유지보수성이 높아졌습니다 복잡한 연관관계 감소 BeanPayDetail의 증감 역할을 PaymentDetail로 통합 가능하다고 판단했습니다. ...

June 3, 2024 · Lee WooJin

락 선택 이유와 성능 테스트

개요 이 포스트는 세 가지 동시성 제어 방식의 성능을 비교합니다: 락 없는 트랜잭션, Beta Lock, Redisson 분산락 테스트 환경 DB: H2 (MariaDB 모드) 환경: 로컬 Redis: Embedded 스레드 수: 32 요청 횟수: 각 1회 1. 트랜잭션만 사용한 결과 @Transactional public void notUseLockTest(String lockName, Integer userId) { BeanPay beanPay = getBeanPay(1, Role.USER); final BeanPayDetail beanPayDetail = BeanPayDetail.ofCreate( beanPay, 1, 5000 ); final BeanPayDetail createBeanPayDetail = beanPayDetailRepository.save(beanPayDetail); beanPay.chargeBeanPayDetail(createBeanPayDetail.getAmount()); } 결과 트랜잭션만 사용하면 MySQL의 기본 격리 수준(Repeatable Read)에서 Lost Update 문제가 발생하여 데이터 정합성이 낮아집니다. ...

April 30, 2024 · Lee WooJin

Github Actions와 AWS CodeDeploy를 활용한 CI/CD 구축

해야할 일 S3 버킷 생성 S3 접근 키 생성 EC2 생성 EC2 규칙 생성 (보안그룹) EC2가 S3, CodeDeploy에 접근하기 위한 역할 적용 EC2 내에 서버를 실행할 수 있게 프로그램 설치 CodeDeploy IAM 권한 설정 S3 버킷 생성 버킷을 생성할 지역을 선정해 주고 생성합니다. S3 접근 키 생성 비밀 엑세스키는 생성 시에 보관해 두어야 합니다. EC2 생성 EC2 규칙 생성 (보안그룹) ...

April 19, 2024 · Lee WooJin

코리아노 BeanPay 고민

빈페이로 결제하게 된 이유 내부 결제 로직과 외부 API 충전 기능을 분리함으로써 외부 API 장애 시 영향을 최소화합니다. 결제를 진행하는 중에는 외부API를 연동하지 않기 때문에 사용자의 경험을 향상시키고 결제 프로세스를 간소화할 수 있습니다. 서비스 자체 포인트인 빈페이 관리 방법 비교 방안 1: 유저 MSA에 beanpay 컬럼 추가 결제 MSA에서 유저 MSA로 API 요청하여 관리. 문제점: 단일 트랜잭션 구현의 어려움 수동 롤백 처리 필요 비관적 락을 사용하거나 분산락을 사용하게 되면 회원의 읽기 성능도 떨어지게 됩니다. 방안 2: 결제 MSA에 관리 (선택) 결제 MSA 내 BeanPay 테이블 및 상세 테이블로 관리. ...

April 17, 2024 · Lee WooJin

[SpringBoot] Spring Batch 이용권 만료

스프링 Batch 구조 Spring Batch는 Job, Step, Reader, Processor, Writer 구조로 이루어져 있습니다. Step 처리 방식 Step은 ItemReader를 사용해 각 아이템을 개별적으로 읽은 후 ItemProcessor에 전달하여 필요한 처리를 수행합니다. 청크 사이즈에 도달할 때까지 반복하고, 완성된 청크를 ItemWriter로 전달합니다. ItemReader ItemReader를 통해 스프링 배치가 아이템 리더에 리드 메소드를 호출하여 해당 메소드는 스텝 내에서 처리할 아이템 한 개를 반환합니다. 대량 데이터 처리 시 메모리 문제를 피하기 위해 두 가지 방식을 사용합니다: Cursor ItemReader: 단일 연결 유지 (장시간 작업 시 연결 끊김 위험) Paging ItemReader: 청크 크기 단위로 페이지를 가져옴 (안정성 우수) ItemWriter 아이템 Writer는 데이터를 쓰는데 사용하며, 개별 아이템이 아닌 Chunk 단위로 씁니다. ...

December 17, 2023 · Lee WooJin

[SpringBoot] 스프링 배치란?

Spring Batch란 일괄처리를 위한 오픈 소스 프레임워크입니다. SpringBatch는 로깅/추적, 트랜잭션 관리, 작업 처리 통계를 포함하여 대용량 레코드 처리에 필수적인 재사용 가능한 기능을 제공합니다. Job 다시 시작, 건너뛰기, 리소스 관리 최적화 및 분할 기술을 통한 대용량 및 고성능 배치 작업 지원 Spring Batch 용어 Job 배치처리 과정을 하나의 단위로 만들어 놓은 객체이며, 배치처리 계층의 최상단에 위치합니다. JobInstance Job 실행의 단위를 나타냅니다. 예를 들어 1월 1일 실행과 1월 2일 실행은 각각의 JobInstance가 생성됩니다. ...

December 16, 2023 · Lee WooJin

주문내역을 내려줄 때 가격 멱등성에 대한 고민

🤔 Problem 기존 제육 1000원 제육 가격 2000원으로 인상 결제했을 때와 주문내역 불일치 메뉴 내역을 내려줄 때 현재 연결되어 있는 메뉴와 옵션을 참조하고 있습니다. 그렇다면 기존의 메뉴의 정보를 변경했다면 어떤 결과가 발생될까 생각이 들었습니다. 주문한 메뉴의 가격을 내려줄 때 메뉴의 가격을 변경하지 않았다면 1000원을 반환했을 겁니다. 하지만 나중에 물가가 오르고 사장님이 메뉴의 가격을 인상했을 때 2000원으로 올리고 손님이 주문내역을 확인하면 2000원으로 나온다는 문제점이 있었습니다. 😃 Solution 저는 이 문제를 해결하기 위해서 2가지 방법을 고안했습니다. ...

October 29, 2023 · Lee WooJin

@WithMockUser 테스트 시 getPrincipal Null 해결

문제 상황 테스트 코드를 작성할 때 Security에서 제공하는 @WithMockUser 어노테이션을 사용해서 인증 테스트를 수행했습니다. 하지만 Authentication의 getPrincipal() 메소드를 사용할 때 null이 반환되는 현상이 발생해서 테스트를 통과하지 못했습니다. 기존의 방식 @GetMapping("/mycomments") public Response<Page<CommentResponse>> getMyComments(Authentication authentication, Pageable pageable) { String username = authentication.getName(); Page<CommentResponse> response = postService.getMyComments(username, pageable).map(CommentResponse::fromComment); return Response.success(response); } authentication 클래스에서 getName()을 통해 서비스단에 유저의 이름을 전달하는 로직이었습니다. 테스트 코드 @Test @WithMockUser(username = "username") @DisplayName("내 댓글 조회 성공") void 내_댓글_조회_성공() throws Exception { //Given String username = "username"; //When when(postService.getMyComments(eq(username), any(Pageable.class))).thenReturn(Page.empty()); //Then mvc.perform(get("/api/v1/post/mycomments")) .andExpect(status().isOk()); } 기존의 authentication.getName() 메소드를 사용할 때는 정상적으로 통과하는 것을 볼 수 있습니다. ...

May 2, 2023 · Lee WooJin