From 211371295d20c2b83b8988e46dc7e8aa9d81e456 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Tue, 6 Jan 2026 11:22:55 +0900 Subject: [PATCH 01/25] =?UTF-8?q?test:=20=EB=B0=B0=ED=8F=AC=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EC=99=80=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EB=B6=88=EC=9D=BC=EC=B9=98=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 7 ++- docker-compose.prod.yml | 60 +++++++++++++++++-- .../domain/member/domain/Member.java | 1 + .../member/repository/MemberRepository.java | 10 ++-- .../service/PublishIssueTokenScheduler.java | 2 +- .../security/utils/HeaderUserIdProvider.java | 1 + src/main/resources/application-local.yml | 12 ++++ src/main/resources/application-prod.yml | 9 --- 8 files changed, 80 insertions(+), 22 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4e53cf7..a9992a8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -54,9 +54,10 @@ jobs: export DB_USERNAME=${{ secrets.DB_USERNAME }} export DB_PASSWORD=${{ secrets.DB_PASSWORD }} - # spring boot 이미지 최신화 및 업데이트 - sudo -E docker-compose -f docker-compose.prod.yml pull app - sudo -E docker-compose -f docker-compose.prod.yml up -d --no-deps app + # 이미지 최신화 및 컨테이너 재시작 + sudo docker-compose pull + sudo docker-compose down + sudo docker-compose up -d --remove-orphans # 필요 없는 이미지 정리 sudo docker image prune -f diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 1acf422..ac8a995 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -7,14 +7,58 @@ services: environment: KAFKA_NODE_ID: 1 KAFKA_PROCESS_ROLES: 'broker,controller' - KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka-1:9093' + KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093' KAFKA_LISTENERS: 'PLAINTEXT://kafka-1:9092,CONTROLLER://kafka-1:9093,PLAINTEXT_HOST://0.0.0.0:29092' KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka-1:9092,PLAINTEXT_HOST://${EC2_PRIVATE_IP}:29092' KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT' KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER' KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT' - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + CLUSTER_ID: 'ciWo7IWazngRchmPES6q5A==' + KAFKA_LOG_DIRS: '/tmp/kraft-combined-logs' + networks: + - app-network + + kafka-2: + image: confluentinc/cp-kafka:latest + container_name: kafka-2 + ports: + - "29093:29093" + environment: + KAFKA_NODE_ID: 2 + KAFKA_PROCESS_ROLES: 'broker,controller' + KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093' + KAFKA_LISTENERS: 'PLAINTEXT://kafka-2:9092,CONTROLLER://kafka-2:9093,PLAINTEXT_HOST://0.0.0.0:29093' + KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka-2:9092,PLAINTEXT_HOST://${EC2_PRIVATE_IP}:29093' + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT' + KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER' + KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT' + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + CLUSTER_ID: 'ciWo7IWazngRchmPES6q5A==' + KAFKA_LOG_DIRS: '/tmp/kraft-combined-logs' + networks: + - app-network + + kafka-3: + image: confluentinc/cp-kafka:latest + container_name: kafka-3 + ports: + - "29094:29094" + environment: + KAFKA_NODE_ID: 3 + KAFKA_PROCESS_ROLES: 'broker,controller' + KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093' + KAFKA_LISTENERS: 'PLAINTEXT://kafka-3:9092,CONTROLLER://kafka-3:9093,PLAINTEXT_HOST://0.0.0.0:29094' + KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka-3:9092,PLAINTEXT_HOST://${EC2_PRIVATE_IP}:29094' + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT' + KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER' + KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT' + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2 KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 CLUSTER_ID: 'ciWo7IWazngRchmPES6q5A==' KAFKA_LOG_DIRS: '/tmp/kraft-combined-logs' @@ -28,9 +72,11 @@ services: - "8090:8080" environment: KAFKA_CLUSTERS_0_NAME: local - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka-1:9092 + KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka-1:9092,kafka-2:9092,kafka-3:9092 depends_on: - kafka-1 + - kafka-2 + - kafka-3 networks: - app-network @@ -57,7 +103,7 @@ services: - "80:8080" environment: SPRING_PROFILES_ACTIVE: prod - SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka-1:9092 + SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka-1:9092,kafka-2:9092,kafka-3:9092 DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_NAME: ${DB_NAME} @@ -68,6 +114,10 @@ services: condition: service_healthy kafka-1: condition: service_started + kafka-2: + condition: service_started + kafka-3: + condition: service_started restart: unless-stopped networks: - app-network diff --git a/src/main/java/com/practice/course_registration/domain/member/domain/Member.java b/src/main/java/com/practice/course_registration/domain/member/domain/Member.java index b6d671f..5696746 100644 --- a/src/main/java/com/practice/course_registration/domain/member/domain/Member.java +++ b/src/main/java/com/practice/course_registration/domain/member/domain/Member.java @@ -9,6 +9,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.DynamicUpdate; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java b/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java index 36ea385..ae70f70 100644 --- a/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java +++ b/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java @@ -12,8 +12,10 @@ public interface MemberRepository extends JpaRepository { Member findByLoginId(String loginId); - @Query("select m from Member m " + - "left join fetch m.memberSubjects " + - "where m.id = :id") - Optional findWithSubjectsById(@Param("id") Long id); + @Query("select m from Member m " + + "left join fetch m.memberSubjects " + + "where m.id = :id") + Optional findWithSubjectsById(@Param("id") Long id); + + Optional findById(long id); } diff --git a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java index 56459e2..6b4e5ad 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java @@ -13,7 +13,7 @@ public class PublishIssueTokenScheduler { private final LuaRepository luaRepository; - private static final int PERMITS_PER_TICKS = 50; // tick당 최대 발급 수 + private static final int PERMITS_PER_TICKS = 100; // tick당 최대 발급 수 private static final int TOKEN_TTL = 5; // token ttl @Scheduled(fixedDelay = 100) diff --git a/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java b/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java index 47ca3f4..1732203 100644 --- a/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java +++ b/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java @@ -17,6 +17,7 @@ public Long getUserId() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String userId = request.getHeader("userId"); + System.out.println("userId = " + userId); if (userId == null || userId.isEmpty()){ throw new ErrorHandler(ErrorStatus._UNAUTHORIZED); } diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 48ddac3..38d0e72 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -33,8 +33,20 @@ spring: web: resources: add-mappings: false +#logging: +# level: +# org: +# hibernate: +# SQL: debug +# apache.tomcat.util.http.Parameters: debug +# springframework.web.servlet.DispatcherServlet: debug server: + tomcat: + threads: + max: 1000 + accept-count: 500 + max-connections: 8192 servlet: session: timeout: 30m # 마지막 활동으로부터 30분 지나면 로그아웃 \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index fb0c32f..8d697e1 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -21,15 +21,6 @@ spring: hibernate: format_sql: true - data: - redis: - host: redis - port: 6379 - connect-timeout: 2000 - database: 0 - - # mvc: - # throw-exception-if-no-handler-found: true web: resources: add-mappings: false From a7f41ac622a004ee37b22a0be019533864a62aa2 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Tue, 6 Jan 2026 11:26:05 +0900 Subject: [PATCH 02/25] =?UTF-8?q?test:=20docker=20compose=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EB=B6=88=EC=9D=BC=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd-test.yml b/.github/workflows/cd-test.yml index 58a17f6..8b0a654 100644 --- a/.github/workflows/cd-test.yml +++ b/.github/workflows/cd-test.yml @@ -63,7 +63,7 @@ jobs: echo "[INFO] Deploying test build with IMAGE_TAG=$IMAGE_TAG" # app만 최신 SHA 태그로 pull & restart - sudo -E docker compose -f docker-compose.prod.yml pull app - sudo -E docker compose -f docker-compose.prod.yml up -d --no-deps app + sudo -E docker-compose -f docker-compose.prod.yml pull app + sudo -E docker-compose -f docker-compose.prod.yml up -d --no-deps app sudo docker image prune -f From 2604369d0e4244ce7774107e77fe877aa792eaa2 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Fri, 23 Jan 2026 00:46:56 +0900 Subject: [PATCH 03/25] =?UTF-8?q?test:=20=EB=8C=80=EA=B8=B0=EC=97=B4=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=BF=BC=EB=A6=AC=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/repository/MemberRepository.java | 3 ++- .../subject/controller/SubjectController.java | 5 +++++ .../domain/subject/domain/MemberSubject.java | 2 +- .../subject/service/SubjectService.java | 6 ++++-- src/main/resources/application-local.yml | 21 ++++++++----------- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java b/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java index ae70f70..3548716 100644 --- a/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java +++ b/src/main/java/com/practice/course_registration/domain/member/repository/MemberRepository.java @@ -13,7 +13,8 @@ public interface MemberRepository extends JpaRepository { Member findByLoginId(String loginId); @Query("select m from Member m " + - "left join fetch m.memberSubjects " + + "left join fetch m.memberSubjects ms " + + "left join fetch ms.subject " + // 어차피 membersubject에서 eager로 subject를 들고와서 N+1 이 생긴다면, 한번에 fetch join 하는 게? "where m.id = :id") Optional findWithSubjectsById(@Param("id") Long id); diff --git a/src/main/java/com/practice/course_registration/domain/subject/controller/SubjectController.java b/src/main/java/com/practice/course_registration/domain/subject/controller/SubjectController.java index b64c4de..7768efc 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/controller/SubjectController.java +++ b/src/main/java/com/practice/course_registration/domain/subject/controller/SubjectController.java @@ -80,8 +80,13 @@ public ResponseEntity> applyCourse(@RequestParam String code Map response = new HashMap<>(); try { + long start = System.currentTimeMillis(); + log.debug("Start to apply course with code {}", code); // 대기열 등록 subjectService.enqueueCourseRequest(memberId, code); + long end = System.currentTimeMillis(); + + log.debug("Apply course request took " + (end - start) + " ms"); // 성공 시 JSON 응답 response.put("status", "WAITING"); diff --git a/src/main/java/com/practice/course_registration/domain/subject/domain/MemberSubject.java b/src/main/java/com/practice/course_registration/domain/subject/domain/MemberSubject.java index 92fed89..3d5ca16 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/domain/MemberSubject.java +++ b/src/main/java/com/practice/course_registration/domain/subject/domain/MemberSubject.java @@ -20,7 +20,7 @@ public class MemberSubject extends BaseEntity { @JoinColumn(name = "member_id") private Member member; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "subject_id") private Subject subject; } diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index 0683025..e78f5a7 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -140,14 +140,16 @@ public void applyCourseWithToken(Long memberId, String code) { * - 신청가능학점을 넘긴경우 -> 위 코드에서 lua 결과로 판단 * */ private void validateCheck(Member member, Subject subject) { - if (memberSubjectRepository.findByMemberAndSubject(member, subject).isPresent()) { + boolean alreadyApplied = member.getMemberSubjects().stream() + .anyMatch(ms -> ms.getSubject().getId().equals(subject.getId())); + + if (alreadyApplied) { throw new ErrorHandler(ErrorStatus.ALREADY_APPLY_SUBJECT); } if (member.getRegisteredScore() + subject.getScore() > MAX_SCORE) { throw new ErrorHandler(ErrorStatus.OVER_SOCRE_POSSIBLE); } - boolean conflict = member.getMemberSubjects().stream() .map(MemberSubject::getSubject) .filter(subj -> diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 38d0e72..23a9f74 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -33,20 +33,17 @@ spring: web: resources: add-mappings: false -#logging: -# level: -# org: -# hibernate: -# SQL: debug -# apache.tomcat.util.http.Parameters: debug -# springframework.web.servlet.DispatcherServlet: debug + +logging: + level: + org: + hibernate: + SQL: debug + apache.tomcat.util.http.Parameters: debug + springframework.web.servlet.DispatcherServlet: debug + com.practice.course_registration: debug server: - tomcat: - threads: - max: 1000 - accept-count: 500 - max-connections: 8192 servlet: session: timeout: 30m # 마지막 활동으로부터 30분 지나면 로그아웃 \ No newline at end of file From 7e9346d34f329bfcc08ebed6e949164de8e388de Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 19:26:37 +0900 Subject: [PATCH 04/25] =?UTF-8?q?test:=20System.out.println=20=EA=B3=BC=20?= =?UTF-8?q?log=20=EC=B6=9C=EB=A0=A5=20X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/subject/service/SubjectQueryService.java | 6 +++--- .../domain/subject/service/SubjectService.java | 2 +- .../global/redis/service/IdempotencyService.java | 2 +- .../global/redis/service/PublishIssueTokenScheduler.java | 2 +- .../global/redis/service/WaitQueueService.java | 4 ++-- .../global/security/utils/HeaderUserIdProvider.java | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectQueryService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectQueryService.java index 4360d12..6de62d4 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectQueryService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectQueryService.java @@ -39,7 +39,7 @@ public class SubjectQueryService { public Page searchAllSubject(Long memberId, CourseFilterRequestDTO filters, Pageable pageable) { - log.info("===========search 시작=============="); + //log.info("===========search 시작=============="); Member member = findById(memberId); String code = nullIfBlank(filters.getCode()); @@ -47,7 +47,7 @@ public Page searchAllSubject(Long memberId, CourseFilterRequ String subjectName = nullIfBlank(filters.getSubjectName()); - log.info("===========code: " + code + " =======professorName : " + professorName + " ========subjectName" + subjectName + " \n"); + //log.info("===========code: " + code + " =======professorName : " + professorName + " ========subjectName" + subjectName + " \n"); Page subjects = subjectRepository.findAllByCodeAndProfessorNameAndSubjectName(code, professorName, subjectName, pageable); @@ -60,7 +60,7 @@ public Page searchAllSubject(Long memberId, CourseFilterRequ List subjectIds = subjects.getContent().stream() .map(Subject::getId) .toList(); - log.info("===========페이지 크기 : " + subjectIds.size()); + //log.info("===========페이지 크기 : " + subjectIds.size()); Set registeredIds = memberSubjectRepository.findAllIdByMemberAndSubject(member, subjectIds); Set likedIds = likeSubjectRepository.findAllByMemberAndSubject(member, subjectIds); diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index e78f5a7..46e71c0 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -74,7 +74,7 @@ public void enqueueCourseRequest(Long memberId, String code) { // 원자성 추가 waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); - log.info("수강신청 접수 성공 (대기열 삽입). Course: {}, Member: {}", subject.getId(), memberId); + //log.info("수강신청 접수 성공 (대기열 삽입). Course: {}, Member: {}", subject.getId(), memberId); } catch (ErrorHandler e) { idempotencyService.releaseIdempotency(memberId, code); diff --git a/src/main/java/com/practice/course_registration/global/redis/service/IdempotencyService.java b/src/main/java/com/practice/course_registration/global/redis/service/IdempotencyService.java index 36a853d..6afbb58 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/IdempotencyService.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/IdempotencyService.java @@ -20,7 +20,7 @@ public class IdempotencyService { public boolean rateLimitAllow(Long memberId, int limit, Duration ttl) { String key = RedisKeyUtils.rateLimitKey(memberId); Long cnt = stringRedisTemplate.opsForValue().increment(key); - System.out.println("cnt : " + cnt); + // System.out.println("cnt : " + cnt); // 첫 요청인 경우 -> TTL 1로 세팅 if (cnt != null && cnt == 1L) { stringRedisTemplate.expire(key, ttl); diff --git a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java index 6b4e5ad..db2cb78 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java @@ -21,7 +21,7 @@ public void issue() { try { long cnt = luaRepository.publishIssueTokens(PERMITS_PER_TICKS, TOKEN_TTL); if (cnt > 0) { - log.info("토큰 발급 성공: count={}, sample={}", cnt); + // log.info("토큰 발급 성공: count={}, sample={}", cnt); } } catch (Exception e) { log.error("토큰 발급 실패", e); diff --git a/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java b/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java index 40c9300..1794168 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java @@ -25,8 +25,8 @@ public void enqueueGlobal(Long memberId, Long subjectId, long nowMillis) { String value = memberId + ":" + subjectId; // payload redisTemplate.opsForZSet().add(queueKey, value, nowMillis); - Long size = redisTemplate.opsForZSet().size(queueKey); - log.info("대기열 등록: {}, queueSize={}", value, size); + // Long size = redisTemplate.opsForZSet().size(queueKey); + // log.info("대기열 등록: {}, queueSize={}", value, size); } // 사용자에게 보여줄 대기열 순번 조회 diff --git a/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java b/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java index 1732203..e04a4ef 100644 --- a/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java +++ b/src/main/java/com/practice/course_registration/global/security/utils/HeaderUserIdProvider.java @@ -17,7 +17,7 @@ public Long getUserId() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String userId = request.getHeader("userId"); - System.out.println("userId = " + userId); + // System.out.println("userId = " + userId); if (userId == null || userId.isEmpty()){ throw new ErrorHandler(ErrorStatus._UNAUTHORIZED); } From 07b52c9c6b09c2a9c8ea9de2a9911ee12e5aca7f Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 19:35:29 +0900 Subject: [PATCH 05/25] =?UTF-8?q?test:=20hikari=20connection=20pool=2060?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=99=95=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 8d697e1..165e70e 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -12,6 +12,8 @@ spring: username: ${DB_USERNAME} password: ${DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 60 jpa: hibernate: From 3d36acaae70f6723e6d85a3dda96251e26dcbb08 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 19:44:18 +0900 Subject: [PATCH 06/25] =?UTF-8?q?test:=20=EC=BB=A4=EB=84=A5=EC=85=98=20?= =?UTF-8?q?=ED=92=80=20X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/subject/service/SubjectService.java | 6 +++--- src/main/resources/application-prod.yml | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index 46e71c0..7bf6843 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -64,13 +64,13 @@ public void enqueueCourseRequest(Long memberId, String code) { boolean held = false; try { // 멤버 찾기 - Member member = findMemberById(memberId); + //Member member = findMemberById(memberId); // 해당 과목 찾기 - Subject subject = findByCode(code); + //Subject subject = findByCode(code); // 유효성 검사 - validateCheck(member, subject); + //validateCheck(member, subject); // 원자성 추가 waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 165e70e..8d697e1 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -12,8 +12,6 @@ spring: username: ${DB_USERNAME} password: ${DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver - hikari: - maximum-pool-size: 60 jpa: hibernate: From 6f7bdf35c95e347ae4c4cd04c622f2ac68745df4 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 19:47:22 +0900 Subject: [PATCH 07/25] =?UTF-8?q?test:=20=ED=9E=88=EC=B9=B4=EB=A6=AC=20?= =?UTF-8?q?=EB=A1=9C=EA=B9=85=20=EB=A0=88=EB=B2=A8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/subject/service/SubjectService.java | 6 +++--- src/main/resources/application-prod.yml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index 7bf6843..46e71c0 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -64,13 +64,13 @@ public void enqueueCourseRequest(Long memberId, String code) { boolean held = false; try { // 멤버 찾기 - //Member member = findMemberById(memberId); + Member member = findMemberById(memberId); // 해당 과목 찾기 - //Subject subject = findByCode(code); + Subject subject = findByCode(code); // 유효성 검사 - //validateCheck(member, subject); + validateCheck(member, subject); // 원자성 추가 waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 8d697e1..e7214fa 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -28,6 +28,10 @@ spring: kafka: bootstrap-servers: ${SPRING_KAFKA_BOOTSTRAP_SERVERS} +logging: + level: + com.zaxxer.hikari: DEBUG + server: servlet: session: From d6ce43798bca4ce18026758dab459e25baec9fe0 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 19:51:51 +0900 Subject: [PATCH 08/25] =?UTF-8?q?test:=20open-in-view=20false=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index e7214fa..1f9cfa0 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -20,6 +20,7 @@ spring: properties: hibernate: format_sql: true + open-in-view: false web: resources: From 3a87b672d930256082e8351e2cf86a261fcba0d9 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 20:55:23 +0900 Subject: [PATCH 09/25] =?UTF-8?q?test:=20Redis=EB=A1=9C=EB=A7=8C=20?= =?UTF-8?q?=EC=A4=84=EC=84=B8=EC=9A=B0=EA=B8=B0=20=ED=99=95=EC=9D=B8/DB=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/subject/service/SubjectService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index 46e71c0..a380667 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -64,16 +64,17 @@ public void enqueueCourseRequest(Long memberId, String code) { boolean held = false; try { // 멤버 찾기 - Member member = findMemberById(memberId); + //Member member = findMemberById(memberId); // 해당 과목 찾기 - Subject subject = findByCode(code); + //Subject subject = findByCode(code); // 유효성 검사 - validateCheck(member, subject); + //validateCheck(member, subject); // 원자성 추가 - waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); +// waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); + waitQueueService.enqueueGlobal(memberId, 1L, System.currentTimeMillis()); //log.info("수강신청 접수 성공 (대기열 삽입). Course: {}, Member: {}", subject.getId(), memberId); } catch (ErrorHandler e) { From dcc862e0a25b3eff40061839b5695dbf1dfdb086 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:11:07 +0900 Subject: [PATCH 10/25] =?UTF-8?q?test:=20redis=20pool=20=EB=8A=98=EB=A6=AC?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../domain/subject/service/SubjectService.java | 8 ++++---- .../global/config/RedisConfig.java | 12 +++++++++++- src/main/resources/application-prod.yml | 5 +++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 6d58ddb..551c6e7 100644 --- a/build.gradle +++ b/build.gradle @@ -61,6 +61,7 @@ dependencies { // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.apache.commons:commons-pool2' // redisson implementation 'org.redisson:redisson-spring-boot-starter:3.27.2' diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index a380667..0c134df 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -64,16 +64,16 @@ public void enqueueCourseRequest(Long memberId, String code) { boolean held = false; try { // 멤버 찾기 - //Member member = findMemberById(memberId); + Member member = findMemberById(memberId); // 해당 과목 찾기 - //Subject subject = findByCode(code); + Subject subject = findByCode(code); // 유효성 검사 - //validateCheck(member, subject); + validateCheck(member, subject); // 원자성 추가 -// waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); + waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); waitQueueService.enqueueGlobal(memberId, 1L, System.currentTimeMillis()); //log.info("수강신청 접수 성공 (대기열 삽입). Course: {}, Member: {}", subject.getId(), memberId); diff --git a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java index 2465320..71ebe1c 100644 --- a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java +++ b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java @@ -4,12 +4,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; @@ -27,7 +29,15 @@ public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); - return new LettuceConnectionFactory(configuration); + + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxTotal(100); + poolConfig.setMaxIdle(20); + + LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder() + .poolConfig(poolConfig) + .build(); + return new LettuceConnectionFactory(configuration, clientConfiguration); } @Bean diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 1f9cfa0..10b20de 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -21,6 +21,11 @@ spring: hibernate: format_sql: true open-in-view: false + data: + redis: + lettuce: + pool: + max-active: web: resources: From ad0a05b48ae3366d3d5a8a95e2ac809e2edbbb7d Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:18:02 +0900 Subject: [PATCH 11/25] =?UTF-8?q?test:=20redis=20lettuce=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EB=90=98=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../course_registration/global/config/RedisConfig.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java index 71ebe1c..f4dafe5 100644 --- a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java +++ b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; @@ -26,8 +27,10 @@ public class RedisConfig { @Value("${spring.data.redis.port:6379}") private int redisPort; + @Primary @Bean public RedisConnectionFactory redisConnectionFactory() { + System.out.println("########## [CHECK] LETTUCE POOL CONFIG ACTIVATED ##########"); RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); From 6a77487be435c66017cef8f88f963344af1865ab Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:19:34 +0900 Subject: [PATCH 12/25] =?UTF-8?q?test:=20redis=20lettuce=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EB=90=98=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 10b20de..1f9cfa0 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -21,11 +21,6 @@ spring: hibernate: format_sql: true open-in-view: false - data: - redis: - lettuce: - pool: - max-active: web: resources: From 71083b7345670d1f341c6c9a6354b2133a3a40d6 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:23:21 +0900 Subject: [PATCH 13/25] =?UTF-8?q?test:=20redis=20lettuce=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EB=90=98=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 1f9cfa0..b63a6ca 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -21,6 +21,12 @@ spring: hibernate: format_sql: true open-in-view: false + data: + redis: + lettuce: + pool: + max-active: 100 + max-idle: 20 web: resources: From 56d9dcb5c73f7fb338600d912cb109fa3cc6bccb Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:43:03 +0900 Subject: [PATCH 14/25] =?UTF-8?q?test:=20redisson=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../global/config/RedisConfig.java | 10 +++++----- src/main/resources/application-prod.yml | 11 +++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 551c6e7..042ac05 100644 --- a/build.gradle +++ b/build.gradle @@ -64,7 +64,7 @@ dependencies { implementation 'org.apache.commons:commons-pool2' // redisson - implementation 'org.redisson:redisson-spring-boot-starter:3.27.2' + // implementation 'org.redisson:redisson-spring-boot-starter:3.27.2' } tasks.named('test') { diff --git a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java index f4dafe5..f6a2f7b 100644 --- a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java +++ b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java @@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; @@ -27,19 +26,20 @@ public class RedisConfig { @Value("${spring.data.redis.port:6379}") private int redisPort; - @Primary @Bean public RedisConnectionFactory redisConnectionFactory() { - System.out.println("########## [CHECK] LETTUCE POOL CONFIG ACTIVATED ##########"); RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); - poolConfig.setMaxTotal(100); - poolConfig.setMaxIdle(20); + poolConfig.setMaxTotal(200); + poolConfig.setMaxIdle(50); + poolConfig.setMinIdle(10); + // 3. Lettuce 설정에 풀 적용 LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder() .poolConfig(poolConfig) .build(); + return new LettuceConnectionFactory(configuration, clientConfiguration); } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b63a6ca..525a8b5 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,4 +1,9 @@ spring: + data: + redis: + host: ${SPRING_DATA_REDIS_HOST:redis} + port: 6379 + h2: console: enabled: false @@ -21,12 +26,6 @@ spring: hibernate: format_sql: true open-in-view: false - data: - redis: - lettuce: - pool: - max-active: 100 - max-idle: 20 web: resources: From 26b9ebf3734e024a9d076e05866e82d588c261e2 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:48:30 +0900 Subject: [PATCH 15/25] =?UTF-8?q?test:=20redisson=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd-test.yml b/.github/workflows/cd-test.yml index 8b0a654..7591a6a 100644 --- a/.github/workflows/cd-test.yml +++ b/.github/workflows/cd-test.yml @@ -64,6 +64,6 @@ jobs: # app만 최신 SHA 태그로 pull & restart sudo -E docker-compose -f docker-compose.prod.yml pull app - sudo -E docker-compose -f docker-compose.prod.yml up -d --no-deps app + sudo -E docker-compose -f docker-compose.prod.yml up -d app sudo docker image prune -f From a836fed19ddd263e83213130d12d408772c7151a Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 21:54:07 +0900 Subject: [PATCH 16/25] =?UTF-8?q?test:=20lettuce=20pool=20=EB=8A=98?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd-test.yml | 2 +- .../global/config/RedisConfig.java | 12 +----------- src/main/resources/application-prod.yml | 6 +++++- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/cd-test.yml b/.github/workflows/cd-test.yml index 7591a6a..8b0a654 100644 --- a/.github/workflows/cd-test.yml +++ b/.github/workflows/cd-test.yml @@ -64,6 +64,6 @@ jobs: # app만 최신 SHA 태그로 pull & restart sudo -E docker-compose -f docker-compose.prod.yml pull app - sudo -E docker-compose -f docker-compose.prod.yml up -d app + sudo -E docker-compose -f docker-compose.prod.yml up -d --no-deps app sudo docker image prune -f diff --git a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java index f6a2f7b..9e3d523 100644 --- a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java +++ b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java @@ -30,17 +30,7 @@ public class RedisConfig { public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); - GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); - poolConfig.setMaxTotal(200); - poolConfig.setMaxIdle(50); - poolConfig.setMinIdle(10); - - // 3. Lettuce 설정에 풀 적용 - LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder() - .poolConfig(poolConfig) - .build(); - - return new LettuceConnectionFactory(configuration, clientConfiguration); + return new LettuceConnectionFactory(configuration); } @Bean diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 525a8b5..79ffefe 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -3,7 +3,11 @@ spring: redis: host: ${SPRING_DATA_REDIS_HOST:redis} port: 6379 - + lettuce: + pool: + max-active: 200 # 최대 200개까지 늘려라 + max-idle: 50 + min-idle: 10 h2: console: enabled: false From f5b977bc790154168db79365418af388b35b7f81 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 22:05:15 +0900 Subject: [PATCH 17/25] =?UTF-8?q?test:=20lettuce=20pool=20=EB=8A=98?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 79ffefe..d3971d3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -8,6 +8,7 @@ spring: max-active: 200 # 최대 200개까지 늘려라 max-idle: 50 min-idle: 10 + enabled: true h2: console: enabled: false From e00bcdac5cbdb9b5960c2f8f9de86fd048df1c28 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 22:11:06 +0900 Subject: [PATCH 18/25] =?UTF-8?q?test:=20lettuce=20pool=20=EB=8A=98?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/RedisConfig.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java index 9e3d523..5bbcd4f 100644 --- a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java +++ b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java @@ -30,7 +30,20 @@ public class RedisConfig { public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); - return new LettuceConnectionFactory(configuration); + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxTotal(200); + poolConfig.setMaxIdle(50); + poolConfig.setMinIdle(10); + + LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder() + .poolConfig(poolConfig) + .build(); + + LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration, clientConfig); + + factory.setShareNativeConnection(false); + + return factory; } @Bean From de4161f38a3945a5bc9b338d737508f74b5e5d86 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 22:17:26 +0900 Subject: [PATCH 19/25] =?UTF-8?q?test:=20tomcat=20=EC=8A=A4=EB=A0=88?= =?UTF-8?q?=EB=93=9C=20=EB=8A=98=EB=A0=A4=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index d3971d3..2964929 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -46,4 +46,8 @@ logging: server: servlet: session: - timeout: 30m # 마지막 활동으로부터 30분 지나면 로그아웃 \ No newline at end of file + timeout: 30m # 마지막 활동으로부터 30분 지나면 로그아웃 + tomcat: + threads: + max: 400 + accept-count: 1000 \ No newline at end of file From 3238bdd676071e181ac67c7ba0beeb1b3de4ed91 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Wed, 28 Jan 2026 23:47:34 +0900 Subject: [PATCH 20/25] =?UTF-8?q?test:=20t3.medium=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=ED=95=98=EB=93=9C=EC=9B=A8=EC=96=B4=20=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EC=98=AC=EB=A6=AC=EA=B3=A0,=20DB=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=97=86=EC=95=A0=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/subject/service/SubjectService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index 0c134df..08d8469 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -64,16 +64,16 @@ public void enqueueCourseRequest(Long memberId, String code) { boolean held = false; try { // 멤버 찾기 - Member member = findMemberById(memberId); + // Member member = findMemberById(memberId); // 해당 과목 찾기 - Subject subject = findByCode(code); + // Subject subject = findByCode(code); // 유효성 검사 - validateCheck(member, subject); + // validateCheck(member, subject); // 원자성 추가 - waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); + // waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); waitQueueService.enqueueGlobal(memberId, 1L, System.currentTimeMillis()); //log.info("수강신청 접수 성공 (대기열 삽입). Course: {}, Member: {}", subject.getId(), memberId); From 61dd9da5661d390643776002b535e89495ae8ac1 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Thu, 29 Jan 2026 00:02:46 +0900 Subject: [PATCH 21/25] =?UTF-8?q?test:=20fixedDelay=20->=20fixedRate?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/subject/service/SubjectService.java | 10 +++++----- .../redis/service/PublishIssueTokenScheduler.java | 2 +- src/main/resources/application-local.yml | 8 +++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java index 08d8469..4dbc3f4 100644 --- a/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java +++ b/src/main/java/com/practice/course_registration/domain/subject/service/SubjectService.java @@ -64,17 +64,17 @@ public void enqueueCourseRequest(Long memberId, String code) { boolean held = false; try { // 멤버 찾기 - // Member member = findMemberById(memberId); + Member member = findMemberById(memberId); // 해당 과목 찾기 - // Subject subject = findByCode(code); + Subject subject = findByCode(code); // 유효성 검사 - // validateCheck(member, subject); + validateCheck(member, subject); // 원자성 추가 - // waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); - waitQueueService.enqueueGlobal(memberId, 1L, System.currentTimeMillis()); + waitQueueService.enqueueGlobal(memberId, subject.getId(), System.currentTimeMillis()); +// waitQueueService.enqueueGlobal(memberId, 1L, System.currentTimeMillis()); //log.info("수강신청 접수 성공 (대기열 삽입). Course: {}, Member: {}", subject.getId(), memberId); } catch (ErrorHandler e) { diff --git a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java index db2cb78..5289186 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java @@ -16,7 +16,7 @@ public class PublishIssueTokenScheduler { private static final int PERMITS_PER_TICKS = 100; // tick당 최대 발급 수 private static final int TOKEN_TTL = 5; // token ttl - @Scheduled(fixedDelay = 100) + @Scheduled(fixedRate = 20) public void issue() { try { long cnt = luaRepository.publishIssueTokens(PERMITS_PER_TICKS, TOKEN_TTL); diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 23a9f74..e6790ae 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -36,12 +36,14 @@ spring: logging: level: + io.lettuce.core.protocol: DEBUG + org.springframework.data.redis: DEBUG org: hibernate: SQL: debug - apache.tomcat.util.http.Parameters: debug - springframework.web.servlet.DispatcherServlet: debug - com.practice.course_registration: debug +# apache.tomcat.util.http.Parameters: debug +# springframework.web.servlet.DispatcherServlet: debug +# com.practice.course_registration: debug server: servlet: From 6eefc1467a5bcaead95ab4b694b4ee2213107683 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Thu, 29 Jan 2026 00:11:16 +0900 Subject: [PATCH 22/25] =?UTF-8?q?test:=20redis=EA=B0=80=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=EC=9D=B8=EC=A7=80=EB=A5=BC=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=EC=84=9C=20scheduled?= =?UTF-8?q?=EB=A5=BC=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=ED=95=B4?= =?UTF-8?q?=EB=B4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/redis/service/PublishIssueTokenScheduler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java index 5289186..72e4765 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/PublishIssueTokenScheduler.java @@ -16,7 +16,7 @@ public class PublishIssueTokenScheduler { private static final int PERMITS_PER_TICKS = 100; // tick당 최대 발급 수 private static final int TOKEN_TTL = 5; // token ttl - @Scheduled(fixedRate = 20) + // @Scheduled(fixedRate = 20) public void issue() { try { long cnt = luaRepository.publishIssueTokens(PERMITS_PER_TICKS, TOKEN_TTL); From e4148137664bf519cc6fd6a6d07178514f4d3f21 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Thu, 29 Jan 2026 00:20:09 +0900 Subject: [PATCH 23/25] =?UTF-8?q?test:=20=ED=86=B0=EC=BA=A3=20=EC=8A=A4?= =?UTF-8?q?=EB=A0=88=EB=93=9C=20=EC=A4=84=EC=97=AC=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 2964929..d3971d3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -46,8 +46,4 @@ logging: server: servlet: session: - timeout: 30m # 마지막 활동으로부터 30분 지나면 로그아웃 - tomcat: - threads: - max: 400 - accept-count: 1000 \ No newline at end of file + timeout: 30m # 마지막 활동으로부터 30분 지나면 로그아웃 \ No newline at end of file From b47988bc586f975a48bae1d3d8a19fb83440a947 Mon Sep 17 00:00:00 2001 From: rud15dns Date: Thu, 29 Jan 2026 00:31:10 +0900 Subject: [PATCH 24/25] =?UTF-8?q?test:=20redis=20<->=20springboot=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EB=A1=9C=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/redis/service/WaitQueueService.java | 4 +++- src/main/resources/application-prod.yml | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java b/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java index 1794168..29f7c65 100644 --- a/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java +++ b/src/main/java/com/practice/course_registration/global/redis/service/WaitQueueService.java @@ -21,10 +21,12 @@ public class WaitQueueService { // 전역 대기열에 요청 삽입 public void enqueueGlobal(Long memberId, Long subjectId, long nowMillis) { + long start = System.currentTimeMillis(); // 측정 시작 String queueKey = RedisKeyUtils.globalApplyQueueKey(); String value = memberId + ":" + subjectId; // payload redisTemplate.opsForZSet().add(queueKey, value, nowMillis); - + long end = System.currentTimeMillis(); // 측정 종료 + log.info("Redis [enqueueGlobal] 소요시간: {}ms", (end - start)); // Long size = redisTemplate.opsForZSet().size(queueKey); // log.info("대기열 등록: {}, queueSize={}", value, size); } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index d3971d3..e87f3b3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -39,10 +39,6 @@ spring: kafka: bootstrap-servers: ${SPRING_KAFKA_BOOTSTRAP_SERVERS} -logging: - level: - com.zaxxer.hikari: DEBUG - server: servlet: session: From 9365b9ad045290cd68c565744983c2cf1f1c488c Mon Sep 17 00:00:00 2001 From: rud15dns Date: Thu, 29 Jan 2026 00:42:29 +0900 Subject: [PATCH 25/25] =?UTF-8?q?test:=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=BB=A4=EB=84=A5=EC=85=98=20=ED=92=80=20=EC=97=86=EC=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/RedisConfig.java | 16 +--------------- src/main/resources/application-prod.yml | 7 +------ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java index 5bbcd4f..d0bde12 100644 --- a/src/main/java/com/practice/course_registration/global/config/RedisConfig.java +++ b/src/main/java/com/practice/course_registration/global/config/RedisConfig.java @@ -29,21 +29,7 @@ public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); - - GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); - poolConfig.setMaxTotal(200); - poolConfig.setMaxIdle(50); - poolConfig.setMinIdle(10); - - LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder() - .poolConfig(poolConfig) - .build(); - - LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration, clientConfig); - - factory.setShareNativeConnection(false); - - return factory; + return new LettuceConnectionFactory(configuration); } @Bean diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index e87f3b3..3fcad90 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -3,12 +3,7 @@ spring: redis: host: ${SPRING_DATA_REDIS_HOST:redis} port: 6379 - lettuce: - pool: - max-active: 200 # 최대 200개까지 늘려라 - max-idle: 50 - min-idle: 10 - enabled: true + h2: console: enabled: false