Tiny Star

springboot 11

[대용량 이관] bookRatingStep 개선

[bookRatingStep]을 만들었다. 300만 데이터 넣는데에 211초가 걸렸고, 문득 성능 테스트하는데 노트북 사양을 full 로 쓰면 조금 아이러니한 느낌이 들어서 메모리를 제한해보고 테스트해보기로 했다. 수정사항1. 메모리 설정-Xms512m -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log옵션설명-Xms512mJVM의 초기 힙 메모리 크기를 512MB로 설정-Xmx1gJVM의 최대 힙 메모리 크기를 1GB로 제한-XX:+PrintGCDetailsGC가 발생할 때 어떤 세대에서 얼마나 수집되었는지 상세하게 로그 출력-XX:+PrintGCDateStampsGC 로그에 날짜와 시간 정보 포함-Xloggc:gc.logGC 로그를 ..

[대용량 이관] Book Job/Step 구성

20만건 정도의 book 데이터를 이관하는 것부터 시작했다. 구성보다는 에러를 해결한 내용이 위주이다...ㅎ Book Job, Step 구성@RequiredArgsConstructor@Configurationpublic class BatchConfiguration { private final Step stepBook; @Bean public Job bookJob(JobRepository jobRepository) { return new JobBuilder("bookJob", jobRepository) .start(stepBook) .build(); }}@Configurationpublic class BookStep ..

[대용량 이관] ItemWriter 선택 (JPA vs JDBC vs MyBatis)

파일 형식에 따라 Reader가 달라지기 때문에 이건 차치하고, 어떤 Writer를 써야할지 고민했다.MyBatisBatchItemWriter만 사용해봤는데, 실무에서 이걸 사용했던 이유는 테이블명이 기간에 따라 변경되기도 하고, if 문을 활용한 분기처리를 위한 동적매핑이 필요했기 때문이다.현재는 동적 매핑이 필요하지 않고 단순 Insert 처리만 하면 되기 때문에 굳이 이걸로 구현해야할까?란 생각이 들었기에 JdbcBatchItemWriter를 고려하게 됐다. JpaItemWriter는 [Spring Batch ItemWriter 성능 비교]를 보고 속도상 제외했다. 하지만!실제로 속도차이가 많이 나는지 궁금해서 테스트해보기로 했다. 구성공통 설정 (Entity)// BookRating.java@N..

[쿠폰] 쿠폰 발급 수정 (saveAll, @Transactional)

for (MapRecord message : messages) { // 중략 CouponIssues issued = CouponIssues.builder() .coupons(Coupons.builder().id(Long.parseLong(couponIdStr)).build()) .userId(userId) .build(); couponIssuesRepository.save(issued); // 중략}기존 코드는 이렇게 CouponIssues 객체 하나씩 인서트하고 있었는데,이렇게하면 Redis 등록하면서 인서트하는거나 마찬가지였다. 그래서 CouponId 기준으로 그룹지어 saveAll 처리하고 그에 따라 Redis Stre..

프로젝트/쿠폰 2025.04.29

[쿠폰] 쿠폰 발급 (Redis Stream, CustomDbWorker)

쿠폰 발급에 관한 상세사항은 [Git Issues] 참고 흐름[1] 발급 요청 수신 ↓[2] Redis 재고 조회 ↓[3] 재고 DECR - 재고 부족이면 → 발급 실패 ↓[4] 발급 성공 - 재고 DECR 상태 유지큰 흐름은 위와 같다.다만 여기에서는 Redis에 해당 Key가 만료되어 사라지거나 다른 문제로 없어졌다면 어떻게 해야할지와DECR만 할 뿐 발급 내역을 따로 저장하는 로직이 포함되어 있지 않아서 추가했다. [1] 발급 요청 수신 ↓[2] Redis 재고 조회 - 없으면 → DB 조회 → Redis 복구 ↓[3] 재고 DECR - 재고 부족이면 → 발급 실패 ↓[4] 발급 성공 - 발급 성공 이력을 Kafka/Redis Stream 등에 publish ..

프로젝트/쿠폰 2025.04.29

[쿠폰] 쿠폰 조회 (Redis 예외처리)

couponId에 따른 재고 조회하는 API (자세한 내용 Git Issue 참고) 1. 요구사항1. 목적Redis에 coupon:{couponId}:stock 형식으로 재고를 등록한다. 2. 작업 내용POST /api/admin/coupons요청 파라미터:couponId (Long, 필수) : 쿠폰 IDquantity (int, 필수) : 초기 재고 수량ttlSeconds (int, 선택) : 재고 만료시간(초 단위), 미지정시 무제한 유지 3. 요청/응답 예시요청POST /api/admin/coupon{ "couponId": 123, "quantity": 100, "ttlSeconds": 86400} 응답{ "couponId":13, "stock":100} 2. 작업 내용//..

프로젝트/쿠폰 2025.04.28

[쿠폰] 쿠폰 생성 (@Transactional, CustomException)

쿠폰 정보를 받아서 DB에 저장하고 Redis에도 올리는 작업을 했다. (자세한 내용은 Git Issue 참고) 1. 요구사항1. 목적쿠폰 등록 시 DB와 Redis를 일관성 있게 관리하기 위해 등록 흐름을 명확히 분리한다.쿠폰 정보를 먼저 DB에 저장하고, 생성된 coupon_id를 기반으로 Redis에 재고를 등록하는 프로세스를 구현한다. 2. 작업 내용쿠폰 종류 등록 API 구현POST /api/admin/coupons API 생성쿠폰 이름(name), 총 수량(total_quantity) 등을 받아 DB에 저장저장 후 생성된 coupon_id를 반환쿠폰 재고 Redis 등록반환된 coupon_id를 사용하여 Redis에 coupon:{coupon_id}:stock으로 재고 등록필요 시 TTL(만..

프로젝트/쿠폰 2025.04.28

[반려동물 프로젝트] 백엔드 수정 (SpringCache)

백엔드 수정 사항region 캐시species 캐시dog/cat List 말고 다른 방식 구현QueryRepository = Service 역할 분리 region + sepecies 합쳐도 50개도 안되기 때문에 Spring Cache를 사용하려고 한다.대용량이거나 이벤트성이었으면 Redis와 Kafka를 고려했을텐데, 전혀 아니니..다음엔 다른 성향의 프로젝트로 위 캐시를 써봐야겠다. 캐시@EnableCaching // 추가@SpringBootApplicationpublic class PetstatsApplication { public static void main(String[] args) { SpringApplication.run(PetstatsApplication.class..

[반려동물 프로젝트] 프론트 구현 (Mustache)

프론트는 JSP 로 하려고 했으나, 뷰에서는 백엔드와는 완전히 분리되는게 좋을 것 같다.(경험을 위한 JSP 는 다른 프로젝트에서 해보는 걸로...) JSP vs Mustache vs Thymeleaf 항목 JSP Mustache Thymeleaf 템플릿 문법자바 코드 삽입 가능 ()순수 템플릿, 로직 없음HTML 태그에 속성(th:)으로 표현문법 표현력강력하지만 복잡단순, 제한적 ({{#each}})풍부하고 직관적 (th:if, th:each)HTML 정합성❌ 깨질 수 있음 (스크립틀릿 등)⛔ HTML로 직접 열면 깨질 수 있음✅ HTML로 열어도 유효한 마크업성능/속도느림 (JSP 컴파일 필요)빠름 (경량 템플릿)중간 (HTML DOM 기반)유지보수성❌ 로직 섞이기 쉬움✅ 표현만 담당, 구..

[반려동물 프로젝트] 백엔드 구현 (QueryDSL)

화면으로 보낼 데이터를 작업하려고 한다.서비스 고민할 때 가장 첫 번째로 떠올렸던 '지역별 대표 품종' 쿼리부터 작업해보자. 의존성 설정val queryDslVersion = "5.0.0"dependencies { implementation("com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta") annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jakarta") annotationProcessor("jakarta.annotation:jakarta.annotation-api") annotationProcessor("jakarta.persistence:jakarta.pers..

top