Spring

    선착순 쿠폰 발급을 위한 redis 분산락

    선착순 쿠폰 발급을 위한 redis 분산락

    선착순 쿠폰 발급을 위한 백엔드 시스템을 구현한 코드는 깃허브에서 볼 수 있다. 🚀 요구사항 멀티 서버 선착순 쿠폰 발급 중복 발급 X 짧은 시간 대용량 트래픽 발생 🚀 구현 기술스택 Language : Java 11 Framework : Spring Boot 2.7.8 Database : MySQL 8.0, JPA, QueryDSL, Redis API Documentation : Swagger 3.0.0 🚀 해결 방법 1. DB Exclusive Lock(배타적 잠금) JPA Pessimistic Lock(비관적 락)을 이용하여 DB에 배타적 잠금 사용 다른 트랜잭션에서 읽기, 수정, 삭제 방지 SQL을 보면 select for update 구문을 사용하는 것을 볼 수 있음 @Lock(LockModeT..

    트랜잭션을 사용할 때 각 DB들의 기본 격리 수준은 무엇일까?

    트랜잭션을 사용할 때 각 DB들의 기본 격리 수준은 무엇일까?

    스프링 프레임워크를 사용한다면 @Transactional 을 Service단에 붙여서 자주 사용할 것이다. 이 경우 아래와 같이 isolation (격리수준)이 default(기본값)으로 지정된다. @Transactional의 옵션인 isolation의 기본값은 Isolation.DEFAULT 라고 한다. 그렇다면 Isolation.DEFAULT란 무엇일까? 우선 Isolation은 TransactionDefinition 인터페이스에 해당하는 Transactional 어노테이션과 함께 사용할 트랜잭션 격리 수준(transaction isolation levels)을 나타내는 열거형(Enumeration)이다. 열거형 값은 다음 5가지가 있다. DEFAULT READ_UNCOMMITTED READ_COMM..

    포스트맨으로 url 요청했는데 405 에러

    포스트맨으로 url 요청했는데 405 에러

    비대면으로 하다보니 원격으로 에러를 고치는 상황이 많은데 역시 화면공유가 제일 빨리 에러를 찾을 수 있는 것 같다. 이번에는 api가 잘 작동하는지 요청을 해보았는데 405 에러가 뜬다고 한다. 포스트맨으로 하면 이렇게 에러 내용을 보기 편하게 만들어줘서 이게 좋은 것 같다. 브라우저는 이렇게 뜨긴 하지만 초보자한테는 포스트맨이 더 좋아보인다..! 포스트맨의 에러 내용을 살펴보면 HTTP 상태코드 405인 Method Not Allowed이다. 메서드가 허용되지 않았다고하며 'GET' 메서드를 지원하지 않는다고 한다. 그리고 해당 경로는 "/api/posts" 이기 때문에 이 메서드를 살펴보면 된다. 위에서는 GET으로 요청했지만 만든 것은 POST 였기 때문에 해당 405 에러가 뜬 것이다. 빠른 해결..

    스프링 부트 의존관계 주입 에러

    스프링 부트 의존관계 주입 에러

    스터디를 하면서 내가 본 적 없는 에러와 예외들이 나와서 기록해보고자 한다. Parameter 0 of constructor in com.example.study.ApiController required a bean of type 'service.MemberService' that could not be found. ApiController 생성자에 필요한 MemberService 타입의 빈을 찾을 수 없다. 따라서 스프링 빈 or 의존관계 자동 주입의 문제라 파악되어서 일단 어노테이션을 잘 붙여서 스프링 빈으로 잘 등록했는지 확인했지만 이 부분은 멀쩡했었다. MemberService 타입의 클래스를 스프링 빈에 등록할 수 있게 @Service 어노테이션이 붙어있었다. 그렇다면 스프링 빈 자동 등록과 의..

    QueryDSL과 1차 캐시 의문점

    QueryDSL이 무엇인지 궁금하시다면 아래 더보기를 눌려주세요. 더보기 QueryDSL이란? 오픈소스 프로젝트로 JPQL을 Java 코드로 작성할 수 있게 해주는 라이브러이다. 왜 사용하는가? Spring Data JPA를 사용하여 CRUD 및 여러 쿼리 메서드 기능을 사용하더라도 결국 커스텀한 쿼리가 필요하게 된다. 이 때 JPQL을 사용하게 되는데 복잡한 로직의 경우 쿼리 문자열이 상당히 길어져서 매우 복잡해진다. JPQL을 잘못작성하더라도 해당 쿼리를 실행하게 되는 코드가 클라이언트(사용자)에 의해서 사용되지 않으면 알 수가 없다. 이러한 문제점을 해결해주고 여러 편의성을 제공한다. JPA를 사용할 때 동적 쿼리와 복잡한 쿼리 문제 해결 가능 쿼리를 문자가 아닌 자바 코드로 작성 가능 문법 오류를..

    스프링부트 - 하루에 한 번 쿠키 기반 조회수 증가 구현

    스프링부트 - 하루에 한 번 쿠키 기반 조회수 증가 구현

    조회수 어뷰징은 어떻게 막아야 할까? 어뷰징 (Abusing) : 의도적인 조작을 통해 조회수나 클릭수를 높이기 위한 일련의 행위 CRUD 커뮤니티 프로젝트를 하면서 고민했던 주제입니다. 다른 프로젝트에서는 커뮤니티 부분을 개발하지 않 ssdragon.tistory.com 조회수 어뷰징은 어떻게 막아야 할까? 라는 의문에서 시작된 조회수 증가 로직 구현하기입니다. 제일 간단하면서도 생각보다 많은 처리를 할 수 있는 쿠키 기반으로 구현해봅니다. 다음과 같은 전제조건이 있습니다. 쿠키 기반 하루에 1번 조회수 증가 비회원도 조회수 증가 구글에 검색해본 결과 하루에 1번 조회수 증가하는 로직을 살펴보았는데 막연히 현재 시간에서 24시간동안만 유지하는 쿠키부터해서 다른 게시글을 들어갈때마다 쿠키 유지시간이 다시..

    @Transactional은 조회만 할 때 있어야할까?

    @Transactional은 조회만 할 때 있어야할까?

    스프링 부트 프로젝트를 진행하면서 트랜잭션 어노테이션을 적지 않은곳에서 조회가 잘되고 있었다. 하지만 어느 곳에서는 읽기 전용 트랜잭션인 `@Transactional(readOnly = true)`를 적어 성능 최적화를 하고 있었다. 여기서 든 의문이 트랜잭션이 애초에 없었으면 읽기 전용으로 안만들어도 되는 것이 아닌가? 였다. 📚 그렇다면 한번 살펴보자 SpringBoot JPA를 사용하고 있고, 단순 조회하는 서비스(Service) 계층이 있다고 가정한다. 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용하고 있다. 이것은 말 그대로 트랜잭션의 범위와 영속성 컨텍스트의 생존 범위가 같다는 의미이다. 트랜잭션 시작 → 영속성 컨텍스트 생성 트랜잭션 끝 → 영속성 컨텍스트 종료 이런..

    deleteAll() vs deleteAllInBatch()

    deleteAll() vs deleteAllInBatch()

    기본기를 다지는 혼자만의 프로젝트에서 잘 쓰이지않는 Spring Data Jpa의 deleteAll() 메서드를 사용하게 되었다. DB에서 데이터의 삭제는 매우 중요한 부분으로 실무에서는 잘 쓰이지 않는다고 하였다. (개인정보는 없애고 나머지 부분은 남긴다던지, 복구라든지 데이터가 필요한 부분이 있기 때문에) 자 그럼 나의 상황을 먼저 간략하게 설명하고 deleteAll()와 deleteAllInBatch()를 비교하면서 동작원리도 파헤쳐보자. 엔티티 구조 Comment 엔티티 (댓글) 댓글과 대댓글은 셀프조인 되어있는 구조 Post 엔티티 (게시글) 현재 상황 본인이 등록한 게시글을 DB에서 삭제하려는 행동을 하려고 한다. 이 때 게시글과 연관되어 있는 댓글을 먼저 삭제하여야 게시글을 삭제할 수 있다...