⏰🚨 Enroll 동시성 테스트에서 신청이 거의 발생하지 않은 문제(JMeter)
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1️⃣ 문제 상황Redis 기반 대기열 시스템에서 **동시성 테스트(JMeter)**를 진행했다.테스트 시나리오:동시 사용자: 100명각 사용자:enqueue 1회SUCCESS가 나올 때까지 /queue/me 폴링SUCCESS 시 enroll 요청기대 결과:정원(capacity) 100명동시 신청 발생DB에 정확히 100건 저장그러나 실제 결과는:02_poll_me 요청은 약 19,000회 이상 실행됨하지만 03_enroll은 5회만 실행H2 DB 확인 결과, ENROLLMENT 테이블에 5건만 저장됨SELECT * FROM ENROLLMENT;결과:총 5건만 저장됨즉, 동시성 테스트가 제대로 작동하지 않았다.2️⃣ 문제 원인 분석① HTTP 요청은 성공했지만, 실제 상태는 SUCCESS가 아니었음JMe..
⏰💡 Enroll 동시성 테스트 + 폴링 API 부하테스트 진행 방법(JMeter)
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
🚨테스트 전에 꼭 해둘 것 3가지🚨(1) 강의 정원(capacity)을 100으로 세팅lecture 테이블에서 capacity를 100으로enrolled(또는 enrolledCount)를 0으로 초기화(2) 스케줄러가 admitted를 풀어줘야 함현재 스케줄러:@Scheduled(fixedDelay = 3000)batchSize =1;이대로면 admitted가 너무 천천히 풀려서 테스트가 오래 걸려.테스트할 때만 임시로 추천:fixedDelay = 1000 (1초)batchSize = 20 또는 50이유: admitted가 빨리 풀려야 “enroll 동시성”이 제대로 발생함.(3) 서버 로그 너무 많으면 느려질 수 있음대량 테스트 때 log.info가 너무 많으면 성능이 왜곡될 수 있어. (가능하면 ..
⏰💡 폴링 API 부하 테스트 (500명 실험)
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
선착순 강의 시스템에서 가장 우려했던 부분은 “폴링 트래픽”이었다. 사용자는 대기 중일 때 1초마다 자신의 순번을 확인한다.동시 사용자가 많아질수록 이 폴링 요청은 기하급수적으로 늘어난다. 그래서 단순히 “잘 되겠지”가 아니라,실제로 JMeter를 통해 부하 테스트를 진행했다.1️⃣ 테스트 시나리오동시 사용자: 500명Ramp-up: 50초사용자 행동:대기열 등록 1회1초 간격으로 30회 폴링즉,500명이 동시에 줄 서고,1초마다 내 순번을 계속 조회하는 상황을 재현했다.2️⃣ 테스트 결과📌 enqueue (대기열 등록)Samples: 901Avg: 3msMax: 11msError: 0%해석평균 3ms최대 11ms에러 0%대기열 등록은 Redis ZSET에 insert하는 단순 연산이기 때문에사용자가 ..
⏰💡 JMeter 구성 요소 해석해보기
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1️⃣ 내가 만든 JMeter 트리는 무엇을 의미할까?내 JMeter 구조는 이렇게 해석하면 된다.가상 사용자 300명이 동시에 접속해서대기열 등록을 한 번 하고1초마다 30번 ‘내 순번 조회’를 반복한다.즉, 실제 사용자 시나리오를 자동으로 재현한 실험이다.2️⃣ JMeter 구성 요소 하나씩 해석하기✅ HTTP Request Defaults여기에 설정한 값:Protocol: httpServer: localhostPort: 8080이건 “모든 요청의 기본 서버 주소”를 설정한 것이다.그래서 개별 요청에서는 경로(Path)만 쓰면 된다:/api/lectures/1/queue/api/lectures/1/queue/me✅ 01_enqueue (POST /queue)이 요청은 “대기열에 줄 서기” 행동을 의미..
⏰💡 폴링 API 부하테스트 진행 방법 (JMeter)
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1️⃣ 준비: 서버 켜져 있어야 함Spring 서버가 localhost:8080에서 실행 중이어야 해.브라우저에서 http://localhost:8080/ 같은 페이지가 뜨면 OK.2️⃣ JMeter 실행 & 새 Test Plan 만들기JMeter 실행왼쪽 트리에서 Test Plan 보일 거야.상단 메뉴 File → Save Test Plan As로 저장해두기 (나중에 날아갈 수 있음)3️⃣ Thread Group 만들기 (가상 사용자 설정)왼쪽 Test Plan 우클릭Add → Threads(Users) → Thread Group오른쪽 설정에서 이렇게 입력:Number of Threads (users): 300 (처음은 100으로 해도 돼. 성공하면 300~1000으로 올리자)Ramp-up period..
⏰💡 선착순 강의에서 비관적 락(PESSIMISTIC LOCK)을 사용한 이유?
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
선착순 강의 신청 기능을 구현하면서 가장 중요했던 문제는 이것이었다.정원이 100명일 때, 동시에 여러 명이 마지막 자리를 신청하면 어떻게 될까?이 문제를 해결하기 위해 나는 비관적 락(PESSIMISTIC_WRITE) 을 사용했다.1️⃣ findByIdForUpdate는 무엇을 하는가?Lecture lecture = lectureRepository .findByIdForUpdate(lectureId) .orElseThrow(() -> new IllegalArgumentException("Lecture not found"));일반적인 findById()는 단순 조회다.하지만 findByIdForUpdate()는 다르다.@Lock(LockModeType.PESSIMISTIC_WRITE)@Quer..
⏰ 선착순 강의 대규모 트래픽 처리 – Redis 2차 설계
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1차 설계에서는 Redis로 대기열을 구현했다.ZSET으로 선착순 보장Scheduler로 순차 입장 처리admitted SET + TTL로 “입장권” 발급하지만 여기서 끝이 아니다.“입장 성공”은 Redis에서의 성공일 뿐,진짜 신청 확정은 DB에 저장되어야 한다.그래서 2차 설계의 목표는 이것이었다.1️⃣ 2차 설계 목표대기열 우회 방지 (입장권 검증)정원 초과 방지 (동시성 제어)중복 신청 방지입장권 1회성 사용 보장실제 신청 기록 영구 저장2️⃣ 전체 구조 흐름Redis 대기열 성공 (admitted) ↓EnrollService.enroll() ↓DB Row Lock (PESSIMISTIC_WRITE) ↓정원 체크 + 차감 ↓Enrollment 저장..
⏰🚨 신청 버튼을 누르지도 않았는데 자동으로 신청되는 문제 (Redis 대기열 + localStorage 이슈)
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1️⃣ 문제 상황선착순 강의 신청 페이지를 구현하던 중 이상한 현상을 발견했다.강의 목록(/lectures)에서“강의 신청하기”를 누르면 상세 페이지로 이동그런데 상세 페이지에 들어가자마자신청 버튼을 누르지도 않았는데 이미 신청된 것처럼 동작어떤 경우에는 사용자가 직접 눌러야 하고,어떤 경우에는 자동으로 신청된 것처럼 보였다.즉,“왜 버튼을 누르지 않았는데 신청이 되는 것처럼 보일까?”라는 의문이 생겼다.2️⃣ 처음에 했던 오해처음에는 이런 생각을 했다.버튼에 onclick이 잘못 걸린 건가?페이지 로드 시 POST /queue가 자동으로 호출되는 건가?브라우저가 버튼을 자동으로 submit하는 건가?하지만 Network 탭을 확인해보니,페이지에 들어가자마자 신청 API(POST /queue) 가 호출되..
⏰ 선착순 강의 대규모 트래픽 처리 – Redis 1차 설계
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1️⃣ 왜 이걸 만들었는가? (문제 정의)내가 만든 “선착순 강의 신청” 기능은 단순 CRUD가 아니라, 본질적으로 이런 문제였다.짧은 시간에 다수 사용자가 동시에 “신청” 버튼 클릭요청 순서를 공정하게 보장해야 함동시에 처리하면 DB에 락/경합 발생 가능사용자는 “내가 신청되었는지”를 계속 확인함 (새로고침/폴링)즉,신청 로직을 DB 트랜잭션에 그대로 몰아넣으면 병목이 생길 수 있다.그래서 해결 방향을 이렇게 잡았다.DB는 최종 확정만 담당“줄서기”는 Redis가 담당빠른 인메모리 자료구조로 병목을 분리2️⃣ 설계에서 가장 많이 고민한 부분(1) 순서 보장을 어떻게 할 것인가?선착순의 핵심은:“누가 먼저 왔는지”를 정렬된 상태로 유지하는 것그래서 Redis의 **ZSET(Sorted Set)**을 사용..
👥🚨 좋아요 상태 변경 후 본인 게시글 삭제 오류 해결 (FK 제약 & DB CASCADE)
·
프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트
1️⃣ 문제 상황본인 게시글을 삭제하려고 했는데 다음과 같은 오류가 발생했다.delete from article where id=?→ FK constraint violation에러의 핵심은 이것이다.article_like 테이블이 article을 FK로 참조 중인데article을 먼저 삭제하려 해서 FK 위반이 발생했다.즉,article_like.article_id → article.id좋아요가 남아 있는 상태에서게시글을 먼저 delete→ DB가 무결성 위반으로 막음좋아요 기능 구조 복습좋아요는 이렇게 설계되어 있다.article_like (user_id, article_id)유니크 제약으로 중복 좋아요 방지Article.likeCount는 캐싱 컬럼문제는:자식 테이블(article_like)을 정리하..