분류 전체보기 51

[ 🚚 ONESTOP ] Spring Boot 모니터링(Prometheus + Grafana) + Swagger JWT 인증 설정

1. 모니터링 — Prometheus + Grafana왜 모니터링이 필요한가?서비스 운영 중에 CPU, 메모리, HTTP 요청 수, 응답 시간, DB 커넥션 풀 상태 등을 수치로 파악하지 못하면 장애가 발생했을 때 원인을 찾기 어렵다.특히 커머스 서비스는 주문/결제 흐름이 복잡해서 병목 지점을 모니터링으로 사전에 파악하는 게 중요하다.기술 선택 이유 — Prometheus + GrafanaSpring Boot Actuator가 /actuator/prometheus 엔드포인트로 메트릭을 Prometheus 포맷으로 노출하면, Prometheus가 주기적으로 수집(scrape)하고, Grafana가 시각화하는 구조다.Spring Boot와 공식 연동: micrometer-registry-prometheus ..

[ 🚚 ONESTOP ] Spring Data JPA 집계 쿼리 - @Query, JPQL, N+1 트레이드오프

구현 기능관리자가 주문/배송 현황을 한 번에 조회할 수 있는 대시보드 API를 구현했다.GET /api/admin/dashboard- 상태별 주문 수 (PENDING_PAYMENT, PAID, CANCELLED)- 오늘 주문 수- 오늘 매출 (취소 주문 제외)- 상태별 배송 수 (ACCEPT, INSTRUCT, DEPARTURE, DELIVERING, FINAL_DELIVERY)이슈에는 /orders와 /deliveries 두 개로 분리하려 했는데, 대시보드 특성상 한 번에 보여주는 게 더 자연스럽다고 판단해서 하나의 엔드포인트로 통합했다.사용한 기술과 선택 이유1. Spring Data JPA 쿼리 메서드주문 수 집계에 사용한 방식long countByStatus(OrderStatus status);l..

[ 🚚 ONESTOP ] CI/CD부터 관리자 API까지 - 기술 선택의 이유

1. CI 빌드 실패 수정문제 상황GitHub Actions CI 빌드가 계속 실패했다. 원인은 세 가지였다.봇이 자동 생성한 DefaultSecurityConfigTest.java가 SecurityConfig가 없다는 전제로 작성되어 있었음CI 환경에는 MySQL과 .env 파일이 없어서 DB 연결 실패Redis 서비스가 CI 환경에 없어서 연결 실패해결 방법DefaultSecurityConfigTest.java 삭제application-test.yml 생성 (H2 인메모리 DB로 대체)OneStopApplicationTests.java에 @ActiveProfiles("test") 추가ci.yml에 Redis 서비스 추가왜 H2를 선택했나?기술장점단점사용하면 좋은 곳H2 인메모리 DB별도 설치 불필요,..

[ 🚚 ONESTOP ] 정책 설계 — 옵션 중복 방지와 상태 관리

1. 상품 옵션 최소 1개 — 무옵션(기본) 처리무옵션 상품도 product_item에 기본 옵션 1개는 있어야 재고 관리가 가능함재고(stock)가 product_item에 있기 때문에 옵션이 0개면 재고를 어디에 붙일지 애매해짐. 무옵션 상품은 아래처럼 처리하기로 결정함option_value_1: "기본"option_value_2~5: nullstock: 1002. Composite Unique Constraint — 옵션 조합 중복 불가동일 상품 내에서 옵션 조합이 중복되면 재고 관리가 불가능해짐예를 들어 색상(빨강) + 사이즈(M) 조합이 두 개 존재하면 어느 쪽 재고를 차감해야 할지 알 수 없음 선택한 방법 - Composite Unique Constraintproduct_item 테이블에 (p..

[ 🚚 ONESTOP ] Docker Compose + GitHub Actions로 팀 개발 환경 & CI 구축하기

1. 로컬 개발 환경 세팅 (Docker Compose)Docker Compose를 선택한 이유로컬에서 MySQL과 Redis를 구성하는 방법이 세 가지 있었음직접 설치 - 가장 단순하지만 팀원마다 버전이 달라질 수 있고 OS별로 설치 방법이 달라 환경 불일치 문제가 생김Docker 단독 실행 - 컨테이너마다 명령어를 따로 입력해야 하고 설정이 코드로 관리되지 않아 팀원 간 공유가 어려움Docker Compose - 하나의 yml 파일로 정의하고 docker compose up -d 한 줄로 실행 가능, 팀원 5명이 동일한 환경을 보장할 수 있어서 선택함이미지 버전을 latest 대신 고정한 이유latest는 팀원마다 설치 시점에 따라 버전이 달라질 수 있어 환경 불일치 문제가 생김redis:7.0, m..

[ 🚚 ONESTOP ] Spring Boot 프로젝트 기술 선택 이유 정리

의존성 선택JWT (jjwt 0.12.6)인증/인가에 세션 대신 JWT를 씀세션 방식은 서버가 상태를 저장해야 해서 서버 여러 대 운영할 때 세션 공유 문제가 생김. JWT는 토큰 자체에 userId, role 정보가 담겨 있어서 서버가 상태를 저장 안 해도 됨. BUYER / SELLER / ADMIN 세 가지 Role을 하나의 토큰으로 구분 가능Spring Session + Redis도 고려했는데, Redis 장애 시 전체 인증이 터지는 리스크가 있어서 제외함QueryDSL 5.1.0상품 조회에서 카테고리, 키워드, 가격 범위, 정렬 조건이 동적으로 조합되는 쿼리가 필요했음JPQL은 조건마다 분기 처리가 복잡하고 문자열을 이어붙이는 방식이라 타입 안전하지 않음. QueryDSL은 Java 코드로 쿼리..

[ 🚚 ONESTOP ] 동시성 제어 전략

재고 차감 — 비관적 락상황주문이 동시에 들어오면 여러 요청이 재고를 동시에 읽고 차감한다. 재고가 1개 남은 상품에 100명이 동시에 주문하면 모두 재고가 1개라고 읽은 뒤 차감을 시도해서 재고가 마이너스가 되는 문제가 생긴다.낙관적 락을 쓰면?재고 차감은 동시 주문이 몰릴 때 충돌이 거의 확실하게 발생한다. 낙관적 락은 충돌이 나면 재시도를 하는데, 100명이 동시에 요청하면 대부분의 요청이 충돌 → 재시도 → 또 충돌을 반복하면서 재시도가 폭발적으로 증가한다. 결국 성능이 오히려 더 나빠진다.Redis 분산락을 쓰면?재고는 DB에 저장되는 데이터다. Redis 분산락으로 락을 잡고 DB에서 재고를 읽고 차감하는 구조가 되는데, 이렇게 하면 Redis → DB 두 번의 네트워크 비용이 발생하고 Red..

[CH 6 실전] 서버 개발 과제

☕ 커피 주문 시스템Spring Boot + JPA + MySQL을 이용해 포인트 기반 커피 주문 시스템을 만들었다.메뉴 조회, 포인트 충전, 주문/결제, 인기 메뉴 집계까지 전 과정을 기록한다.설계 내용ERDusers├── id BIGINT PK AUTO_INCREMENT└── point INTmenus├── id BIGINT PK AUTO_INCREMENT├── name VARCHAR└── price INTorders├── id BIGINT PK AUTO_INCREMENT├── user_id BIGINT FK → users.id├── total_price INT└── created_at DATETIMEorder..

스케줄러와 Quartz (타임세일 스케줄러 기술 선택)

1. 배경특가 상품은 지정된 시작/종료 시간에 맞춰 상품 상태를 정확히 변경해야 하는 요구사항이 있었음이를 구현하기 위한 스케줄러 기술을 선택하는 과정에서 아래 네 가지 방식을 검토함 2. 검토한 방식 1) 조회 시점 계산 방식상품 조회 시 현재 시간과 saleStartTime, saleEndTime을 비교해 판매전 / 판매중 / 판매종료 상태를 동적으로 판단하는 방식장점: 구조가 단순하고 스케줄러가 필요 없음단점조회 시 BETWEEN saleStartTime AND saleEndTime 조건이 들어가는데, 이 경우 인덱스를 제대로 활용하지 못해 상품 수가 많아질수록 풀스캔이 발생할 수 있음서버 시간과 DB 시간이 다를 경우, 동일한 상품이 서버에서는 판매중으로 보이고 DB에서는 판매전으로 처리되는 불일..

[플러스 Spring] 코드 개선 과제

1. @Transactional의 이해 1) 문제사항 + 원인POST /todos 호출 시 아래 에러 발생Connection is read-only. Queries leading to data modification are not allowedTodoService 클래스 레벨에 @Transactional(readOnly = true) 가 선언되어 있어서 클래스 안의 모든 메서드가 읽기 전용 트랜잭션으로 실행saveTodo()는 DB에 INSERT가 필요한 쓰기 작업인데, 읽기 전용 설정과 충돌해 에러 발생// 문제 코드 — saveTodo()도 읽기 전용으로 실행됨@Service@Transactional(readOnly = true)public class TodoService { public Tod..