Backend Developer

성혁

도메인 중심 설계와 문제 해결을 즐기는 Java/Spring 백엔드 개발자입니다.

Language
Java 21 Python C C++
Framework
Spring Boot Spring Security
Database
MySQL Redis
ORM
JPA QueryDSL
Tools
Git GitHub Actions Docker Jira Figma
PROJECT_01
🛒 Home Shopping Backend
Spring Boot 기반 홈쇼핑 백엔드 서버 — 개인 프로젝트
2025 개인
Java 21 Spring Boot Spring Security JWT JPA Redis MySQL
  • JWT 기반 인증/인가 — Access Token + 커스텀 예외 처리로 401/403 상태 코드 분리 응답
  • 재고 차감 동시성 처리@Lock(PESSIMISTIC_WRITE)로 DB 레벨 배타적 락 적용, 3초 타임아웃 설정으로 무한 대기 방지
  • 선착순 쿠폰 발급 시스템 — Redis INCR 원자성으로 수량 관리, SADD로 중복 발급 방지, DB 저장 실패 시 Redis 수동 롤백 처리
  • 상품 조회 Redis 캐싱@Cacheable / @CacheEvict로 캐시 적용 및 무효화, 재고 변경 시 @Caching으로 item/itemList 캐시 동시 evict
  • 글로벌 예외 처리@ControllerAdvice로 커스텀 예외 계층 구성, 비즈니스 상황에 맞는 HTTP 상태 코드 응답
  • 입력값 검증@Valid + Bean Validation으로 요청 DTO 검증, MethodArgumentNotValidException 핸들러로 400 응답
⌥ GitHub 바로가기
PROJECT_02
📚 Third Tool Backend
학습 카드(플래시카드) 관리 서비스 백엔드 — 팀 프로젝트
2025.09
Java 21 Spring Boot JPA QueryDSL MySQL Elasticsearch AWS S3
  • 태그별 월간 학습 통계 조회 APIUserCardEntity → Card → Deck → Tag JPQL 조인으로 이번 달 태그별 학습 횟수 집계, Stats BC 신규 구축 및 테스트 코드 작성
  • 덱 공유 라이브러리 기능isShared 필드 및 originalDeck 자기참조 관계 설계, 덱 공개/비공개 전환 및 카드 포함 덱 복사 기능 구현
  • 다중 태그 AND 조건 검색 — 단일 태그 Long에서 List<Long> 복수 태그 필터링으로 확장, JPQL IN 조건 활용
  • 덱 이름 자동완성 검색LIKE :keyword% JPQL + Pageable로 scope(공개/개인)별 최대 10개 결과 제한
  • 태그 서비스 리팩토링NameNormalizer 유틸 도입으로 NFKC 유니코드 정규화 처리, 대소문자 무시 태그 중복 방지 로직 강화
⌥ GitHub 바로가기
동시성 선착순 쿠폰 발급 시 수량 초과 발급 문제
문제
선착순 쿠폰 발급 시 여러 사용자가 동시에 요청하면 수량을 초과해 발급되는 레이스 컨디션이 발생했습니다.
원인
여러 스레드가 동시에 남은 수량을 조회하고, 각자 차감하면서 초과 발급이 발생했습니다. DB 비관적 락은 트래픽이 몰리는 쿠폰 발급 상황에서 병목이 심하게 발생할 것으로 판단했습니다.
해결
Redis INCR 명령어의 단일 스레드 원자성을 활용해 수량을 관리했습니다. SADD로 중복 발급을 체크하고, DB 저장 실패 시 catch 블록에서 Redis를 수동 롤백하도록 처리했습니다. 또한 @Transactional 메서드 내부에서 Redis를 함께 사용하면 DB 롤백 시 Redis가 롤백되지 않는 문제가 있어, DB 저장 로직을 별도 CouponIssueService로 분리해 Spring 프록시를 통한 트랜잭션이 정상 동작하도록 했습니다.
결과
동시 요청에도 수량 초과 발급이 발생하지 않으며, DB 장애 시 Redis 상태도 정합성을 유지합니다.
버그 주문 시 재고가 두 번 차감되는 문제
문제
주문 요청 시 재고가 실제보다 2배 차감되는 버그가 발생했습니다.
원인
OrderService에서 itemService.reduceStock()을 명시적으로 호출하면서, 동시에 OrderItem.createOrderItem() 내부에서도 item.removeStock()이 호출되고 있었습니다. 재고 차감이 두 곳에서 중복 실행된 것입니다.
해결
도메인 객체인 OrderItem.createOrderItem() 내부에서 재고를 차감하는 것이 도메인 로직이 응집된 올바른 구조라 판단하고, OrderService의 명시적 reduceStock() 호출을 제거했습니다.
결과
재고가 정확히 1회만 차감되며, 도메인 로직이 엔티티에 응집된 구조를 유지했습니다.
캐시 상품 수정 후 캐시에 구 데이터가 남는 문제
문제
상품을 수정하거나 재고가 변경돼도 조회 시 이전 데이터가 반환되는 문제가 발생했습니다.
원인
saveItem, updateItem, deleteItem@CacheEvict가 누락되어 있었고, 특히 재고 차감(reduceStock) 시 item 캐시는 evict하면서 itemList 캐시는 evict하지 않아 목록 조회에서 품절 전 데이터가 남아있었습니다.
해결
@Caching 어노테이션으로 itemitemList 캐시를 동시에 evict하도록 수정했습니다. 캐시 관련 로직은 ItemService에서만 관리하도록 책임을 분리했습니다.
결과
상품 변경 시 관련 캐시가 즉시 무효화되어 항상 최신 데이터가 반환됩니다.
유니코드 한글 태그 중복 허용 문제
문제
동일한 한글 태그명이 중복 생성되는 문제가 발생했습니다. "Java"와 "java", 혹은 자모 분리된 형태가 서로 다른 태그로 인식됐습니다.
원인
한글 입력 방식에 따라 유니코드 표현이 달라지는 문제(자모 분리)와 대소문자 차이를 고려하지 않고 단순 문자열 비교로 중복을 체크했기 때문입니다.
해결
NameNormalizer 유틸 클래스를 도입해 태그 저장 시 NFKC 유니코드 정규화를 적용하고, 소문자로 변환한 nameKey를 별도로 관리해 중복 비교에 활용했습니다.
결과
입력 방식이나 대소문자와 관계없이 동일한 의미의 태그는 중복 생성되지 않습니다.