도메인 중심 설계와 문제 해결을 즐기는 Java/Spring 백엔드 개발자입니다.
@Lock(PESSIMISTIC_WRITE)로 DB 레벨 배타적 락 적용, 3초 타임아웃 설정으로 무한 대기 방지INCR 원자성으로 수량 관리, SADD로 중복 발급 방지, DB 저장 실패 시 Redis 수동 롤백 처리@Cacheable / @CacheEvict로 캐시 적용 및 무효화, 재고 변경 시 @Caching으로 item/itemList 캐시 동시 evict@ControllerAdvice로 커스텀 예외 계층 구성, 비즈니스 상황에 맞는 HTTP 상태 코드 응답@Valid + Bean Validation으로 요청 DTO 검증, MethodArgumentNotValidException 핸들러로 400 응답UserCardEntity → Card → Deck → Tag JPQL 조인으로 이번 달 태그별 학습 횟수 집계, Stats BC 신규 구축 및 테스트 코드 작성isShared 필드 및 originalDeck 자기참조 관계 설계, 덱 공개/비공개 전환 및 카드 포함 덱 복사 기능 구현Long에서 List<Long> 복수 태그 필터링으로 확장, JPQL IN 조건 활용LIKE :keyword% JPQL + Pageable로 scope(공개/개인)별 최대 10개 결과 제한NameNormalizer 유틸 도입으로 NFKC 유니코드 정규화 처리, 대소문자 무시 태그 중복 방지 로직 강화INCR 명령어의 단일 스레드 원자성을 활용해 수량을 관리했습니다. SADD로 중복 발급을 체크하고, DB 저장 실패 시 catch 블록에서 Redis를 수동 롤백하도록 처리했습니다. 또한 @Transactional 메서드 내부에서 Redis를 함께 사용하면 DB 롤백 시 Redis가 롤백되지 않는 문제가 있어, DB 저장 로직을 별도 CouponIssueService로 분리해 Spring 프록시를 통한 트랜잭션이 정상 동작하도록 했습니다.OrderService에서 itemService.reduceStock()을 명시적으로 호출하면서, 동시에 OrderItem.createOrderItem() 내부에서도 item.removeStock()이 호출되고 있었습니다. 재고 차감이 두 곳에서 중복 실행된 것입니다.OrderItem.createOrderItem() 내부에서 재고를 차감하는 것이 도메인 로직이 응집된 올바른 구조라 판단하고, OrderService의 명시적 reduceStock() 호출을 제거했습니다.saveItem, updateItem, deleteItem에 @CacheEvict가 누락되어 있었고, 특히 재고 차감(reduceStock) 시 item 캐시는 evict하면서 itemList 캐시는 evict하지 않아 목록 조회에서 품절 전 데이터가 남아있었습니다.@Caching 어노테이션으로 item과 itemList 캐시를 동시에 evict하도록 수정했습니다. 캐시 관련 로직은 ItemService에서만 관리하도록 책임을 분리했습니다.NameNormalizer 유틸 클래스를 도입해 태그 저장 시 NFKC 유니코드 정규화를 적용하고, 소문자로 변환한 nameKey를 별도로 관리해 중복 비교에 활용했습니다.