본문 바로가기

전체 글25

동시성 테스트와 테스트 초기화를 위한 @Transactional 사용의 생각 이전에 Volunteer 프로젝트의 동시성 테스트 코드를 작성하면서 예상과 다른 결과가 발생한 상황이 있었다. 그땐 문제를 단순히 해결하는 데만 급급했고, 정확한 원인을 정리하는데 집중하지 않았다. @Transactional 롤백 테스트에 대해서 느낀 점과 함께 기록해보고자 한다. 1. 동시성 테스트 시 문제 상황 먼저 문제의 테스트 코드를 살펴보면, 아래와 같이 테스트에 @Transactional 이 사용되는 것을 볼 수 있다. 상황: 여러 회원이 동시에 일정 참여를 시도할 때, 참여 가능 인원 임계값을 넘어가는 상황 해결: 추가 데드락 이슈로 인해 비관적 락을 사용한 상태 일반적으로 테스트 코드에서 데이터 롤백을 위해서 @Transactional을 습관적으로 사용한다. 그럼 현재 상황에서 테스트가 성.. 2024. 2. 21.
밸리데이션 정책에 전략 패턴을 적용해보았다 해당 포스터는 Volunteer 프로젝트의 관한 내용입니다. 객체지향 프로그래밍에서 추상화는 많은 이점을 제공한다. 인터페이스, DI, 추상 클래스 등은 런타임에 구체 클래스가 정해지므로 유연한 확장과 의존성이 줄어든다는 점이 존재한다. 이처럼 구현을 하면서 if문 분기 처리가 많이 나오거나, 추상화가 가능한 부분이 나온다면 추상화를 이용한 패턴을 고려해 본다. 프로젝트를 리팩터링 하면서 유효성 검사에 분기처리가 필요했고, 추상화와 밀접하게 연관된 전략 패턴을 적용해 보았다. 전략 패턴의 개념은 다음과 같다. 실행(런타임) 중에 다형성을 기반으로 알고리즘 전략을 선택하여, 동적으로 전략을 수정할 수 있는 행위 디자인 패턴. 어떤 행동을 수행하는 알고리즘이 여러 개 있을 때, 동작을 미리 전략으로 정의함으.. 2024. 2. 9.
시간여행 테스트 1. 서론 Volunteer 프로젝트를 리팩토링 하면서 현재 시간을 기준으로 마감된 일정을 조회하기 위해 LocalDate.now()를 사용했다. LocaDate.now()는 현재 시간에 의존하기 때문에 테스트하기 어려운 코드이다. 이럴 경우 Controller 계층으로 밀어내고, 나머지 계층에선 메서드 인자(LocalDate)로 받아서 테스트하기 좋은 코드(순수 함수)로 만든다. 즉, 제어하기 힘든 코드는 최대한 가장 바깥쪽 영역(프로그램의 진입 영역)으로 밀어내고 의존하는 범위를 좁히는 것이다. 하지만, 위의 방식에서도 문제점이 여전히 존재한다. Controller 계층도 아직 테스트하기 어렵다. 제어하기 힘든 코드가 Controller 계층에 몰리게 된다. 나머지 계층(Service, Reposit.. 2024. 2. 5.
JPA saveAll()의 N+1 문제 해당 포스터는 Volunteer 프로젝트의 내용입니다. 발생 원인 JPA saveAll()를 사용할 때, Bulk Insert를 기대했으나 기대와는 다르게 엔티티 각각 INSERT 쿼리가 발생되었다. 즉, N개의 엔티티를 저장했을 때 INSERT 쿼리가 N개가 발생한다. 먼저, saveAll() 메서드 내부 구조를 조금 더 자세히 알아보자. saveAll() 메서드 내부적으로 루프를 통해 결국 save() 메서드를 사용한다. 영속성 컨텍스트에서 ID가 null(0L) 인지를 판별해서 새로운 객체임을 판단하고, 새로운 엔티티일 경우 영속화를 진행한다. 이때, INSERT 쿼리가 발생하는 것을 예상할 수 있다. (실제 DEBUG을 했을 때도 즉시 INSERT 쿼리가 발생했다.) 💡 그럼 JPA를 사용할 때,.. 2024. 2. 1.
페이징 쿼리 개선-커버링 인덱스 인턴 간 맡은 도메인의 페이징 쿼리를 개선해 본 내용입니다. 1. 상황 아래는 도메인 이해를 위해 간략히 배송 도메인의 ER 다이어그램을 나타낸 것이다. 주문 상품을 단위로 배송이 등록 여러 주문 상품이 하나의 배송에 매핑 가능(묶음 배송) 그럼 배송 조회 페이징에 사용되는 쿼리를 살펴보자. SELECT dp.id, d.created_at, d.last_transport_status, d.delivery_company, d.invoice_no, op.order_product_status, op.product_id, op.name, op.order_product_bundle_id, op.delivery_type, ore.name, ore.street_address, ore.detail_street_addr.. 2024. 1. 17.
우아한테크코스6기 3주차 미션 회고록 2주 차 미션때와 동일하게 공통 피드백과 크루원들의 코드 리뷰를 통해 부족한 부분을 많이 배울 수 있었다. 많은 크루원들의 코드를 보면서 MVC 패턴, 일급 컬렉션, DTO 등을 정말 잘 사용하시는 분들이 많았고 살짝 위축된 부분도 있었다. 하지만, 누구든 속도는 다르다고 생각하기 때문에, 하나씩 채우자는 마음으로 3주 차 미션을 임했다. 3주 차 미션도 2주 차와 마찬가지로 https://github.com/woowacourse-precourse/java-lotto-6를 fork/clone 한 후, 요구사항을 만족해 미션 코드를 작성하는 것이었다. 구매한 로또와 당첨 로또의 통계 결과를 만드는 것이 핵심이었다. 이번 미션은 이전 미션과 다르게 확연히 난이도가 올라간 것을 체감할 수 있었다. 제 코드는 .. 2023. 11. 8.
우아한테크코스6기 프리코스 2주차 미션 후기 1주 차가 미션이 끝나고, 공통 피드백과 크루원들의 코드 리뷰를 통해 자아성찰을 할 수 있는 부분이 많았다. Getter의 사용, 알고 보니 2가지 이상의 일을 담당했던 메서드, 유틸리티 클래스 등 스스로 놓친 부분과 개념들이 의외로 많았고, 2주 차 미션에서는 실수를 반복하지 않기 위해 회고하는 시간을 가졌다. 자세한 내용은 아래 링크를 참고해주세요. https://bonsik.tistory.com/21 2주 차 미션도 1주 차와 마찬가지로 https://github.com/woowacourse-precourse/java-racingcar-6 를 fork/clone 한 후, 요구사항을 만족해 미션 코드를 작성하는 것이었다. n대의 자동차를 전진 조건을 통해 전진해서 우승자를 맞추는 것이 핵심 기능이었고.. 2023. 11. 1.
우아한테크코스6기 프리코스 1주차 미션 후기 작년에 우아한테크코스를 처음 알게 되었고, 몰입이라는 교육 철학이 너무나 인상 깊어 이번 프리코스에 참여하기로 결정했다. 우아한테크코스의 몇 가지 후기를 보았을 때, 매 기수 미션은 동일한 거 같았다. 그래서 미션과 관련된 내용들이 나오면 창을 닫았었고, 프리코스 미션들을 온전히 내 힘으로 풀고, 부족한 점을 발견하자는 생각으로 프리코스에 참가했다. 1주 차 미션은 https://github.com/BonSik-Koo/java-baseball-6 에 적힌 야구 게임을 fork/clone 한 후, 요구사항을 만족해 미션 코드를 작성하는 것이었다. 제가 작성한 코드는 https://github.com/woowacourse-precourse/java-baseball-6/pull/947 를 참고 바랍니다. 1주.. 2023. 10. 24.
원시값 포장과 VO 스타트업 인턴 도메인을 파악하면서 VO의 개념 정립할 수 있었고, 스스로 공부한 내용을 기록해보고자 한다. (참고로 해당 서비스는 도메인과 엔티티가 분리된 환경이였다.) 원시값 포장이란? 원시값 포장이란, String, int 등 원시 타입(Primitive)의 값을 이용해 속성을 표현하지 않고, 의미있는 객체로 포장해서 사용한다는 개념이다. 간단히 아래와 같다고 볼 수 있다. String name = "홍길동"; //안티 패턴 Name name = new Name("홍길동"); name 에는 다양한 유효성 검사 등이 존재할 수 있는데, 외부에서 검사하는 것 아니라, 이러한 검사를 Name 클래스에게 책임을 넘기게 된다. 그럼 원시값을 사용하는 이유가 뭘까? VO 라는 객체로 포장했기 때문에, 클래스 내.. 2023. 9. 23.