본문 바로가기

Spring

Spring Event 사용하기 사용자의 포인트 로직을 설계하고 개발하는 과정에서, 도장판을 생성하는 요청, 회원 가입하는 요청 등 특정 기능이 성공적으로 완료되면 포인트가 적립되는 2 가지의 동작을 해야 하는 경우가 발생했다. 2 가지의 동작을 하나의 메서드에서 구현하게 되면, 도장판 생성 기능과 포인트 적립 기능이 강한 결합(Tight Coupling)이 된다. 이는 로직을 관리하기가 어렵고, 특정 기능에 문제가 발생하였을 때, 해당 문제를 처리하는 로직 역시 섞이게 되어 가독성이 떨어지고 유지보수 측면에서 좋지 않다고 생각했다. ✨ 문제 코드 @Service @RequiredArgsConstructor public class StampBoardService { private final StampBoardRepository stam.. 더보기
Jpa Validate 에러 (with. Oracle DB, h2) 프로젝트를 진행하며, Local, Dev, Prod 환경에서는 Oracle Database를 사용하고, Test 환경에서는 H2 데이터 베이스를 사용했다. ✔ Entity @Entity @Table(name = "users") @SequenceGenerator(name = "USERS_SEQ_GENERATOR", sequenceName = "USERS_SEQ", initialValue = 1, allocationSize = 1) class User( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ_GENERATOR") val id: Long = 0L, @Column(nullable = false, updatabl.. 더보기
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.. 더보기
Kotlin + Spring DTO에서 NotNull 변수의 null Validation 📒 상황 data class FacilityReq( @NotNull val category: Category, @NotBlank val name: String ) ◼ 시설 등록 요청 DTO를 위와 같이 작성하고 name 값을 넣지 않고 요청을 보내면 에러가 발생한다. 💥 기대했던 에러는 name은 null이 될 수 없다는 에러였는데, 에러 API 응답으로 설정한 값과 다른 값이 응답으로 왔다. 📒 원인 name을 NotNull 타입인 String으로 설정해서 null을 허용하지 않는 타입으로 정의했다. 이로 인해, name이 null로 채워지게 된다면 @field:NotBlank validation 로직을 타기 전에 변수에 값을 할당하는 과정에서 예외가 발생하게 되어 다음과 같은 에러가 발생한다. 💥 H.. 더보기
[Spring] checkstyle 적용하기 📒 왜? ◼ 팀프로젝트를 진행하면서, 코딩 컨벤션을 구성하고 잘 지키는 것이 협업의 기본이라고 생각한다. ◼ 코드 리뷰 시, 통일된 코드 구성의 가독성과 로직 이해에 도움이 될 것 같아서 lint 설정을 알아보게 되었다. 💬 sonarlint vs checkstyle sonarlint는 IDE 플러그인으로 코딩 컨벤션을 제공해 주기 때문에, 개인이 설정 파일을 설정해주어야 하고, 이러한 설정 파일을 팀원들과 공유하면서 사용해야 한다. 또한, 해당 방식은 코딩 컨벤션에 어긋난 상태로 pull-request를 등록해도 자동으로 merge를 강제할 수 있는 방법이 없다. 반면, checkstyle은 gradle build를 진행할 때, 등록한 코딩 컨벤션에 맞게 코딩 컨벤션을 체크할 수 있으며, 프로젝트에 파.. 더보기
[JPA] 설정한 batch_size만큼 in 절이 나가지 않는 이유 @BatchSize(size = 100)으로 설정했음에도 불구하고 실제 조회 쿼리를 실행하면 아래와 같은 실행 결과가 나왔다. select emojilist0_.comment_no as comment_5_3_1_, emojilist0_.emoji_no as emoji_no1_3_1_, emojilist0_.emoji_no as emoji_no1_3_0_, emojilist0_.board_no as board_no4_3_0_, emojilist0_.comment_no as comment_5_3_0_, emojilist0_.emoji as emoji2_3_0_, emojilist0_.user_no as user_no3_3_0_ from tb_emoji emojilist0_ where emojilist0_.c.. 더보기
[Spring] Reactive Web (4) CompletableFuture Java 8에서 부터 사용 가능하다. 비동기 작업 결과 Future -> 비동기 작업의 결과를 담고 있는 Object ListenableFuture -> Callback 구조로 결과 처리 CompletableFuture -> 다양한 비동기 작업을 간단하게 수행할 수 있다. Future, CompletionStage를 구현하고 있다. CompletionStage -> 완료한 결과에 의존적으로 다른 작업을 실행할 수 있는 기능을 제공한다. 별도의 스레드 풀을 가질 수 있다. 리스트의 모든 값이 완료될 때까지 기다리거나 하나의 값만 완료되길 기다릴지 선택할 수 있다. 람다 표현식, 파이프 라인닝을 활용해 Callback Hell 개선 제공 메서드 CompletableFuture.. 더보기
[Spring] Reactive Web (3) Callback Hell 개선 | 코드 future.addCallback(data -> { ListenableFuture future2 = asyncRestTemplate.getForEntity( Service2Uri, String.class, data.getBody() ); future2.addCallback(data2 -> { ListenableFuture future3 = myServiceV2.work(data.getBody()); future3.addCallback( data3 -> dr.setResult(data3), ex -> dr.setErrorResult(ex) ); }, ex -> { dr.setErrorResult(ex); }); }, ex -> { dr.setErrorResult(ex.. 더보기