Backend 썸네일형 리스트형 동시성 문제와 해결 방법들 - 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 값을 활용하여 접근해 좋아요 수 증가 및 조회.. 더보기 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에서 Rest Docs 문서화 코드 개선하기 기존에는 테스트 결과를 문서화 하기 위해, Rest Docs에서 제공하는 FieldDescriptor를 다음과 같이 생성하였다. 그러다 보니 Field가 많으면 엄청난 양의 코드를 작성해야 하고 이 부분의 가독성이 떨어져 개선하고자 한다. ❌ 문제의 코드 restDocMockMvc.post("/test") { jsonContent(request) }.andExpect { status { isOk() } }.andDo { handle( MockMvcRestDocumentation.document( "{class-name}/test-success", requestFields( fieldWithPath("name").description("이름").attributes(field(EXAMPLE, request... 더보기 Kotest를 통한 DCI 패턴 적용 프로젝트를 진행하면서 Controller 테스트를 작성하였고, 해당 코드에 대해 리뷰어로부터 다음과 같은 리뷰를 받게 되었다. 기존 테스트 코드는 다음과 같은 형태로 작성하였다. @WebMvcTest(TestController::class) class TestControllerTest : ControllerTestHelper() { @Test fun `테스트 API - 성공`() { --- } @Test fun `테스트 API - 실패 (name is empty)`() { --- } } 왜 리뷰어께서 위와 같은 리뷰를 남기셨는지 의문이 들었고, 의문을 바탕으로 알아본 DCI 패턴에 대해 작성하고자 한다. DCI 패턴이란? DCI 패턴은 BDD 테스트 코드 작성 패턴으로, BDD 패턴은 코드의 행동을 설명.. 더보기 spring에서 flyway 사용하기 ◼ flyway는 데이터 베이스의 현상관리를 목적으로 하는 툴이다. ◼ 데이터 베이스의 DDL 이력을 쌓아서 DDL이 어떻게 변화되었는지 관리하고, 사용하는 데이터 베이스의 스키마 버전을 관리할 수 있다. 💬 스키마의 이력관리와 프로덕션 코드 작성 시, SQL 문도 함께 작성하여 배포 시 자동으로 DB 스키마 수정이 가능하도록 하기 위해 사용한다. 사용하기 ✔ build.gradle.kts 파일에 다음과 같은 의존성을 추가한다. implementation("org.flywaydb:flyway-core") implementation("org.flywaydb:flyway-mysql") 💥 MariaDB 10.6 이상 버전을 사용하는데 implementation("org.flywaydb:flyway-mysql.. 더보기 [Spring] checkstyle 적용하기 📒 왜? ◼ 팀프로젝트를 진행하면서, 코딩 컨벤션을 구성하고 잘 지키는 것이 협업의 기본이라고 생각한다. ◼ 코드 리뷰 시, 통일된 코드 구성의 가독성과 로직 이해에 도움이 될 것 같아서 lint 설정을 알아보게 되었다. 💬 sonarlint vs checkstyle sonarlint는 IDE 플러그인으로 코딩 컨벤션을 제공해 주기 때문에, 개인이 설정 파일을 설정해주어야 하고, 이러한 설정 파일을 팀원들과 공유하면서 사용해야 한다. 또한, 해당 방식은 코딩 컨벤션에 어긋난 상태로 pull-request를 등록해도 자동으로 merge를 강제할 수 있는 방법이 없다. 반면, checkstyle은 gradle build를 진행할 때, 등록한 코딩 컨벤션에 맞게 코딩 컨벤션을 체크할 수 있으며, 프로젝트에 파.. 더보기 이전 1 2 다음