public List<Double> getPayInfo(@RequestBody BookingPaymentsRequest bookingPaymentsRequest) {
List<Double> result = bookingInquiryService.getPricesAccAvailable(
DateInfo.of(bookingPaymentsRequest),
DistanceInfo.of(bookingPaymentsRequest)
);
result.sort(Comparator.naturalOrder());
return result;
List 이라는 타입으로 바로 리턴해주는데요, dto 클래스로 한번 래핑해서 보내주면 어떨까요??
DTO 응답 객체에만 컨트롤러가 의존하게 하고 List나 Double 같은 타입이 바로 노출되는것 대비해서 확장성, 캡슐화에도 좋고 스웨거 API 문서화를 할때도 좋습니다!
@Override
@Lock(LockModeType.PESSIMISTIC_WRITE)
동시성 프로그래밍에서 Lock 을 쓰실때는 상당히 조심하셔야 됩니다. 그 이유는 PESSIMISTIC_WRITE 를 쓰게 되면 for update 가 붙어서 데이터베이스에 row 수준의 lock 을 발생시키는데요. 이 상황이 발생하게 되면 해당 전체 row 에 읽기 또는 쓰기가 원천 차단됩니다.
즉, 이 lock 이 걸린 기간 동안 lock 이 걸린 row 에 접근 하는 모든 쿼리가 blocking 됩니다. 만약 트래픽이 많다면 어플리케이션 전체 성능을 떨어뜨리거나 timeout 을 무수히 많이 발생시킬 수 있게 되고, 스레드 고갈 문제로도 이어지게 됩니다. 그래서 사실 정말 중요한 로직이 아니면 lock 은 데이터 베이스 수준으로 걸지 않는게 좋으며, 조금 더 다른 방법으로 처리해볼 수도 있습니다.
예를 들면, "예약 중" 이라는 상태를 만들고, 후에 배치 어플리케이션이나 다른 프로세스를 통해 확인 한 후 예약이 있었으면 취소하는 방법이죠. 실제로 이렇게 비동기 형태로 돌아가는 형태의 어플리케이션이 많습니다. 아니면 앞단에서 조금 더 빠르며, 싱글 스레드로 돌아 동시성 문제를 줄여주는 Redis 를 이용해봐도 됩니다.
데이터베이스 Lock (feat. transaction)

동시성(concurrency)이라는 것은 여러 사용자나 응용 프로그램이 동시에 데이터베이스에 접근하여 작업을 수행할 수 있음을 의미한다. 이 때 동시성은 한 행(row)에 접근하는 것 뿐만 아니라, 여러 행과 테이블 전체에 대한 접근 또한 포함하므로 동시성 문제는 굉장히 쉽게, 자주 일어날 수 있다.
동시에 같은 데이터를 두 사람이 수정한다면 나중에 저장한 사람의 정보만 남게 되어 앞서 입력한 정보는 사라지게 된다. 이런 상황은 중요한 정보가 사라지는 결과를 초래할 수 있으며, 잘못된 정보에 기반한 결정을 내리게 할 수도 있다. 따라서, 데이터의 일관성과 무결성을 유지하기 위해 동시성을 효과적으로 관리하는 것은 굉장히 중요하다.

Lock은 db에서 동시에 수행되는 여러 작업 사이에
특정 데이터 항목에 대한 접근을 제어한다. 읽기 또는 쓰기를 수행하는 동안 다른 트랜잭션이 그 데이터를 수정하지 못하게 된다.
락에는 낙관적 락과 비관적 락이 있다.
충돌이 빈번하게 발생하는 경우에는 비관적 락을 쓴다. 데이터를 읽거나 수정하기 전에 명시적으로 Lock을 걸어서 다른 트랜잭션이 해당 데이터에 접근하지 못하도록 한다.
은행 시스템에서 두 사용자가 동시에 같은 계좌에서 돈을 인출하려 할 때, 첫 번째 트랜잭션은 계좌에 Lock을 걸고 인출 작업을 완료한 후 Lock을 해제한다.
이 기간 동안 두 번째 트랜잭션은 Lock이 해제될 때까지 기다려야 하며, 이는 동시성은 감소시키지만 데이터의 일관성과 무결성을 보장한다.
낙관적 락은 데이터 충돌이 적을 때 적합하다. 데이터 수정 작업을 마치기 직전에 데이터가 변경됐는지를 확인한다. 변경 사항이 감지되면 트랜잭션을 롤백하고 재시도 한다.
사용자가 웹 기반의 문서 편집기에서 문서를 편집할 때, 각 사용자의 변경 사항은 문서에 커밋하기 전에 최종 버전과 비교된다. 이 방법으로, 다른 사용자가 동일한 문서를 동시에 수정하고 있어도 변경 사항이 서로 덮어쓰기 되지 않는다.
비관적 락의 한 형태로, db 테이블에 개별 행에 대해서 락을 설정한다. 최소한의 자원만을 잠그므로 교착 상태가 발생할 확률이 적다.
Shared Lock (S-Lock)과 Exclusive Lock (X-Lock)은 'Row level locking'에서 가장 일반적으로 사용되는 두 가지 유형이라고 한다.
S-Lock은 데이터를 읽을 때 설정된다. 다른 트랜잭션이 동시에 해당 데이터를 읽을 수 이쏟록 허용하지만, 해당 데이터에 대한 쓰기 작업은 제한한다.
X-Lock은 다른 트랜잭션이 해당 데이터를 읽거나 쓰지 못하게 한다.
1)Read Uncommitted
가장 낮은 격리 수준으로, 다른 트랜잭션이 커밋하지 않은 데이터를 읽어버리는 ‘Dirty Read’가 발생할 수 있다.
2)Read Committed
트랜잭션에 의해 커밋된 데이터만 읽을 수 있어 'Dirty Read'는 방지할 수 있다.
단, 하나의 트랜잭션내에서 똑같은 SELECT 쿼리를 실행했을 때 다른 결과를 가져오는 'Non-Repeatable Read'가 발생할 수 있다.
3)Repeatable Read
트랜잭션이 시작할 때 읽은 데이터를 트랜잭션 동안 일관되게 유지한다. 따라서 'Non-Repeatable Read'는 방지할 수 있다.
단, 트랜잭션에 따라 다른 데이터를 응답하는 'Phantom Read'는 여전히 발생할 수 있다.