목록spring (9)
wgrgwg-dev
프로젝트에 감정 분석 AI 로직 추가하기 (Google Gen AI SDK for Java)배경꿈일기 게시판 서비스 'Somniverse'의 핵심 기능인 꿈일기는 사용자가 자신의 꿈을 기록하는 기능이다. 하지만 단순히 텍스트를 저장하는 것만으로는 사용자에게 충분한 피드백을 주기 어려웠다. 사용자에게 자신이 꾼 꿈이 어떤 감정 상태를 반영하는지, 꿈의 분위기가 어땠는지 직관적으로 알려주는 기능을 추가하고 싶었다. 당연히 텍스트를 분석하여 감정을 추출하는 과정은 단순 로직으로는 구현이 어렵다. 한 두 문장이 아닌 여러 문장들로 이루어진 꿈일기의 복잡한 문맥과 뉘앙스를 파악하는데에 단순한 방식으로는 한계가 분명하다. 따라서, 문맥을 이해하고 추론할 수 있는 LLM을 도입하기로 했다. 도입에도 여러 방법이 있겠..
Bucket4j로 Rate Limit 도입하기배경서버는 정상 트래픽 외에도 잘못된 클라이언트 구현, 봇 트래픽, 인증 시도 폭주 등으로 짧은 시간에 과도한 요청이 몰리는 상황이 발생할 수 있다. 단순 캐싱이나 프론트 제어만으로는 한계가 있고, 공정성(여러 사용자 또는 작업이 서버의 자원을 공평하게 분배받고, 특정 주체에 의해 불합리한 이득이나 불이익을 받지 않도록 보장)과 안정성을 위해 이를 방지할 방법이 필요했다. 지난번에는 POST 재시도 문제를 줄이기 위해서, 멱등키를 도입했었다. 멱등키는 여러번 보내지는 같은 의미의 같은 POST 요청에 대해서 최초 한 번만 처리하고, 나머지는 기존 결과를 재생함을 보장한다. 이를 통해 데이터 정합성에 도움이 되고, 서버의 부담을 줄여준다. 하지만, 다음과 같은 ..
POST 요청 재시도에 멱등키 적용하기배경결제, 주문처럼 한 번만 실행됨을 보장해야 하는 POST 요청은 네트워크 재시도, 사용자의 더블 클릭, 모바일 환경의 네트워크 이슈 등 여러 이유로 동일 요청이 중복되기 쉽다. 실제로 꿈 일기 게시판 프로젝트 Somniverse에서도 네트워크 속도 이유로 동일한 요청이 두 번 발생해서 두 개의 중복된 게시물이 작성된 적이 있다. 따라서 같은 의도의 같은 요청은 여러번 와도 한 번만 처리하도록 보장할 방법이 필요했다. 사실 더블 클릭의 경우는 전송 직후 버튼을 비활성화하는 방식 등으로 프론트에서 막을 수 있을 것이다. 하지만 여전히 네트워크 레벨에서의 재시도가 존재할 수 있고 데이터의 정합성에 대한 책임으 백엔드에 있다고 판단했다. 따라서 프론트 방지 + 백엔드 ..
⚠️ 문제 상황`@WebMvcTest`를 이용해 `DreamController` 테스트를 작성하던 중, 다음과 같은 `NullPointerException`이 발생했다.java.lang.NullPointerException: Cannot invoke "dev.wgrgwg.somniverse.security.userdetails.CustomUserDetails.getMember()" because "userDetails" is null at dev.wgrgwg.somniverse.dream.controller.DreamController.createDream(DreamController.java:39) 이 예외는 결국 아래와 같은 테스트 실패로 이어졌다:java.lang.AssertionError: Sta..
⚠️ 문제 상황소셜 로그인을 도입하고 난 후, 본래 정상 작동하던 `@WebMvcTest`를 사용하는 `AuthController` 테스트에서 다음과 같은 오류가 발생했다. Error creating bean with name 'SecurityConfig': Unsatisfied dependency expressed through constructor parameter 1: No qualifying bean of type 'CustomOAuth2UserService' available 🔍 원인 분석`@WebMvcTest`는 컨테이너 레이어에 필요한 빈들만 최소한으로 로딩하는 슬라이드 테스트이다. 따라서, `@Service`, `@Repository` 등은 기본적으로 테스트 컨텍스트에 포함되지 않으며, ..
기존 예외 처리 전략회원가입 로직에 대해서 이메일이 중복되는 경우 예외 처리를 다음과 같이 관리했었다. - `MemberService`@Transactionalpublic MemberResponseDto signup(MemberSignupRequestDto memberSignupRequestDto) { if (memberRepository.existsByEmail(memberSignupRequestDto.email())) { throw new EmailAlreadyExistsException(); } ...} - `UsernameAlreadyExistsException`public class UsernameAlreadyExistsException extends Runtim..
@WebMvcTest`@WebMvcTest`란?`@WebMvcTest`는 Spring MVC 컨트롤러(`@Controller`, `@RestController`)를 테스트하기 위한 전용 *슬라이드 테스트 어노테이션 이다. 이 어노테이션으로 웹 계층만 빈으로 등록해서 보다 가볍게 테스트를 수행할 수 있다. *슬라이드 테스트 : 특정 영역의 레이어 테스트 왜 사용할까?컨트롤러 단위의 테스트에는 관련 컨텍스트만 로딩되면 되는데, 애플리케이션의 전체 컨텍스트를 로딩하는 `@SpringBootTest`는 테스트 시간이 오래 걸리고, 불필요한 컴포넌트까지 초기화하게 된다. 따라서, `@WebMvcTest`은 다음과 같을 때 사용할 수 있다.컨트롤러 로직에 집중한 테스트를 원할 때불필요한 서비스, 리포지토리, 설정 ..
MockitoMockito란?Mockito는 Java에서 단위 테스트를 작성할 때 사용하는 Mocking Framework이다.객체 간의 상호작용을 가짜(Mock) 객체를 통해 검증할 수 있도록 도와주며, 의존성을 가진 클래스의 동작을 시뮬레이션하여 테스트하고자 하는 클래스에만 집중할 수 있도록 해준다. 왜 사용할까?MemberService의 테스트 코드를 작성한다고 가정해보자.여기서 MemberService는 당연하게도 MemberRepository에 의존한다. 단위 테스트를 할 때 실제 DB에 접근하면 테스트의 속도나 안정성이 떨어지는데, Mockito를 이용해서 MemberRepository를 가짜(mock) 객체로 대체해서 다음과 같은 이점을 얻을 수 있다.외부 의존성을 제거하여 테스트 속도 향상..