본문 바로가기

전체 글

Redisson 분산락 고도화 💥 Redisson Pub/Sub을 이용한 분산락을 프로젝트에 적용하면서, 분산락이 필요한 기능마다 Facade 객체를 만들거나 부가적인 기능을 제공하는 코드를 계속해서 작성해야 하는 문제가 발생했다.@Componentclass LockPostLikeFacade( private val redissonClient: RedissonClient, private val postLikeService: PostLikeService,) { fun increase(postId: Long) { val lock = redissonClient.getLock(postId.toString()) try { val available = lock.tryLock(5, 3,.. 더보기
동시성 문제와 해결 방법들 - 2편 (분산락 with. Database Lock, Redis) 지난 포스팅에서 동시성 문제 해결 방법으로 Synchronized에 대해서 알아보았다. Synchronized를 활용한 동시성 해결은 분산 환경에서는 활용될 수 없다. 이번 포스팅에서는 데이터 베이스 Lock, 레디스를 활용한 분산락을 통해 동시성을 해결하는 방법에 대해서 알아보고자 한다. ✔️ Pessimistic Lock 활용 - Database Level 데이터 베이스에서 지원하는 Pessimistic Lock(비관적 락)을 활용하여 실제 데이터 베이스에 Exclusive Lock을 걸어서 데이터 정합성을 보장할 수 있다. 🔗 Exclusive Lock 배타적 잠금, 쓰기 잠금이라고 불린다. 한 트랜잭션에서 데이터를 변경하거나 쓰고자 할 때, 해당 트랜잭션이 완료될 때까지 레코드(row), 테이블(.. 더보기
동시성 문제와 해결 방법들 - 1편 (Synchronized) ✔️ 문제 코드 @Entity class PostLike( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0L, @Column(nullable = false) val postId: Long, likeCount: Long, ) { @Column(nullable = false) var likeCount: Long = likeCount private set fun increase() { this.likeCount += DEFAULT_INCREASED_LIKE_COUNT } } 테스트를 위한 도메인 구성으로 id 값과 postId 값 두 개를 생성했고, 편의성을 위해 postLikeId 값을 활용하여 접근해 좋아요 수 증가 및 조회.. 더보기
[DB] 문자열 데이터 타입에 글자(한글) 수 제한하기 create table post( ... title varchar(30) not null ... )ENGINE=InnoDB DEFAULT CHARSET=utf8; DB(MySQL)에 게시글을 저장하는 기능을 구현하면서, post 테이블을 생성하였다. 게시글을 저장할 때의 요구 사항은 제목의 최대 글자는 10자였고, utf-8 버전의 한글은 3byte를 차지하므로 데이터 타입을 varchar(30)으로 정의하였다. 💬 UTF-8 UTF-8은 유니코드 문자를 인코딩하는 가변 길이 문자 인코딩 방식 중 하나로, 8비트 기반의 문자 인코딩 방식이다. ASCII 문자(영어...)는 1byte로 표현하고, 한글과 같은 유니코드 문자는 3바이트로 표현한다. insert into post(title) values('테.. 더보기
Spring Event 사용하기 사용자의 포인트 로직을 설계하고 개발하는 과정에서, 도장판을 생성하는 요청, 회원 가입하는 요청 등 특정 기능이 성공적으로 완료되면 포인트가 적립되는 2 가지의 동작을 해야 하는 경우가 발생했다. 2 가지의 동작을 하나의 메서드에서 구현하게 되면, 도장판 생성 기능과 포인트 적립 기능이 강한 결합(Tight Coupling)이 된다. 이는 로직을 관리하기가 어렵고, 특정 기능에 문제가 발생하였을 때, 해당 문제를 처리하는 로직 역시 섞이게 되어 가독성이 떨어지고 유지보수 측면에서 좋지 않다고 생각했다. ✨ 문제 코드 @Service @RequiredArgsConstructor public class StampBoardService { private final StampBoardRepository stam.. 더보기
kotlin + HttpInterface 알아보기 Spring Boot 3.x(Spring 6.x) 이상부터 새롭게 추가되었고, 공식적으로 사용을 권장하는 HttpInterface에 대해 알아보고자 한다. HttpInterface란? ✔ HttpInterface는 Http 요청을 위한 로직을 전통적인 RestTemplate, WebClient를 깡으로 사용하여 개발하는 것이 아닌, 인터페이스와 어노테이션을 통해 정의할 수 있도록 지원한다. (Feign Client와 유사하다) ✔ 구현한 인터페이스를 프록시 객체로 생성하고, 이를 Bean으로 등록하여 사용하면 손쉽게 Http 요청을 보낼 수 있다. implementation("org.springframework.boot:spring-boot-starter-webflux") ◼ 해당 Dependency를 .. 더보기
kotlin + Jpa Lazy Loading 트러블 슈팅 상황 1 Kotlin과 JPA를 사용하면서, 다음과 같은 plugins를 적용하고 LazyLoading을 사용하였으나, 적용되지 않는 문제가 발생하였다. kotlin("plugin.spring") version "1.8.21" kotlin("plugin.jpa") version "1.8.21" 🔎 plugin.spring: 다음과 같은 어노테이션이 붙은 코틀린 클래스에 대해 자동으로 붙는 final 키워드를 open 해주는 allOpen을 지원한다. Spring boot 2.x 버전부터 CGLIB Proxy 방식으로 Bean을 관리하고 있어, Target Class를 상속받아 프록시를 생성하기 때문에 open으로 열기 위해 사용한다. @Component, @Async, @Transactional, @Ca.. 더보기
클라이언트-서버 cookie 사용 문제 상황 Jwt 웹 토큰을 사용하면서, RefreshToken은 보안 상의 문제로 Cookie를 통해 프론트 서버로 전송하려고 상황에서 프론트에서 cookie를 사용하지 못하는 에러 상황이 발생했다. ❗ 초기 Cors 설정 registry .addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true); Set-Cookie ResponseCookie refreshTokenCookie = ResponseCookie.from(REFRESH_TOKEN_HEADER, refreshToken) .httpOnly(true) .maxAge(refreshExpiredTimeMs).. 더보기