• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

ProductNumber 계산 로직

23.10.05 10:57 작성 조회수 256

0

  • 안녕하십니까!
    먼저 항상 좋은 강의 감사드립니다.
    특히 이번 강의에서는
    Transaction에서 readOnly 옵션을 사용하므로써
    JPA 최적화를 통해 성능 향상을 이점을 얻을 수 있다와 함께,
    CQRS 측면을 말씀해 주셔서 좋았습니다!

    저의 경우는 아직 master/slave DB 구조를 다뤄본적이 없어서 몰랐는데,
    이렇게 readOnly 옵션을 사용할 경우 DB endpoint를 구분하여 명시하므로써 master/slave DB 구조를 사용할 수 있다는 측면도 알게되어서 좋았습니다.

    본론으로 넘어가서,

  • Q1. ProductNumber를 계산하는 로직을
    DB에서 최신값을 읽어온 후에 ,
    그 값을 기반으로 다음 productNumber 값을 계산하는 로직을 저의 경우는 동시성 이슈를 고려하여 ,
    1.일단 SaveAndFlush()로 Id를 할당받고

     

    2. 그 Id 값으로 만들자 라는 로직을 사용하였습니다.
    (IDENTITY 전략이므로 테이블당 1부터 Id가 할당되기 때문에 가능)

    이러한 저의 로직에 문제가 있는지 피드백 주시면 정말 감사하겠습니다.
    스크린샷 2023-10-05 오전 10.49.27.png

  • 특히 calculateProductNumber() 메소드를 저의 경우는 도메인 주도 전략에 기반하여 서비스의 private Method로 정의하지 않고 , Entity 안에 정의하여 사용하는 편인데,
    이러한 스타일에 대해서도 문제가 없을지 여쭤보고 싶습니다.

Q2.두번째로 저의 경우는 createProduct() 메소드 테스트를

1.처음 생성

  1. 일의자리 productNumber로 생성

  2. 십의자리 productNumber로 생성

  3. 백의자리 productNumber로 생성

  4. 1000개를 넘어가면 예외발생

으로 구분하여 테스트 하였습니다.

그런데 이 테스트를 한꺼번에 돌렸을 땐

직전 테스트로 이미 저장된 Product에 의해 다음 테스트에 영향을 미쳐 일부 테스트가 실패하는 상황이 발생하였습니다.

이상황을 어떻게 해결할 수 있을지 질문 드리고 싶고

(sleep() 으로 간격을 주어야 할까요,,)

또한 이 원인이 테스트를 한꺼번에 실행시킬 경우,

각 테스트 메소드가 각 쓰레드로 동시에 실행되기 때문인지

여쭤보고 싶습니다!

 

감사합니다.

 

답변 2

·

답변을 작성해보세요.

1

안녕하세요, khd1692 님! :)
답변 드리겠습니다.

 

Q1. ProductNumber를 계산하는 로직을 DB에서 최신값을 읽어온 후에 ,
그 값을 기반으로 다음 productNumber 값을 계산하는 로직을 저의 경우는 동시성 이슈를 고려하여 ,
1.일단 SaveAndFlush()로 Id를 할당받고
2. 그 Id 값으로 만들자 라는 로직을 사용하였습니다.
(IDENTITY 전략이므로 테이블당 1부터 Id가 할당되기 때문에 가능)
이러한 저의 로직에 문제가 있는지 피드백 주시면 정말 감사하겠습니다.

특히 calculateProductNumber() 메소드를 저의 경우는 도메인 주도 전략에 기반하여 서비스의 private Method로 정의하지 않고 , Entity 안에 정의하여 사용하는 편인데,
이러한 스타일에 대해서도 문제가 없을지 여쭤보고 싶습니다.

2가지 정도를 말씀드릴 수 있을 것 같은데요.

먼저, saveAndFlush() 대신 단순히 save()한 반환값을 받아서 사용해도 insert 쿼리가 나갈거에요.
IDENTITY 전략이라 영속성 컨텍스트에서 PK가 필요하기 때문에 바로 insert 쿼리를 수행합니다.
(한번 디버거 걸어서 확인해 보셔요. ㅎㅎ)

Product savedProduct = productRepository.save(product);

두 번째로, Entity 안에 정의하는 것 자체는 좋습니다. ㅎㅎ
그럼 setter 대신 이렇게 쓰면 더 간단할 것 같아요.

savedProduct.createProductNumber();

이미 productNumber를 생성할 수 있는 전략과 데이터를 모두 product가 가지고 있기 때문에, 굳이 외부로 값을 반환한 다음 setter를 열어 세팅할 필요는 없을 것 같아요. :)

 

Q2.두번째로 저의 경우는 createProduct() 메소드 테스트를

1.처음 생성
2. 일의자리 productNumber로 생성
3. 십의자리 productNumber로 생성
4. 백의자리 productNumber로 생성
5. 1000개를 넘어가면 예외발생

으로 구분하여 테스트 하였습니다.

그런데 이 테스트를 한꺼번에 돌렸을 땐
직전 테스트로 이미 저장된 Product에 의해 다음 테스트에 영향을 미쳐 일부 테스트가 실패하는 상황이 발생하였습니다.
이상황을 어떻게 해결할 수 있을지 질문 드리고 싶고 (sleep() 으로 간격을 주어야 할까요,,)
또한 이 원인이 테스트를 한꺼번에 실행시킬 경우, 각 테스트 메소드가 각 쓰레드로 동시에 실행되기 때문인지 여쭤보고 싶습니다!

어떤 상황에서 어떻게 실패하는지, 코드나 에러 메시지 등을 공유주시지 않아서 제가 정확히 알기는 어려운데요. ㅎㅎ

일단 직전 테스트로 이미 저장된 Product에 의해 다음 테스트에 영향을 미쳐 일부 테스트가 실패하는 상황이 발생한 시점에서부터, 테스트 케이스에 대한 고민을 다시 해보는 것이 좋겠습니다.
즉, 아래 2가지 질문을 던져볼 수 있을 것 같아요.

  • 단순히, 각 테스트가 서로 독립적이게 할 수 있는 장치가 없을지? (이미 고민해 보셨겠지만)

  • 내가 설계한 테스트 케이스 자체가 테스트끼리 영향받을 수 밖에 없도록 설계한 것인지?

2번째 질문에 입각해서, 저라면 테스트 케이스를 다음과 같이 설계할 것 같아요.

  • Product를 저장하고 나서, productNumber를 생성하는 메서드를 호출했을 때, 부여받은 Product의 ID를 기반으로 productNumber를 규칙에 따라 생성한다.

    • given : Product 저장하기. 그리고 저장한 product의 ID 값 확인. (실제로 어떤 값인지는 모르고, 알 필요도 없음)

    • when : productNumber를 생성하는 메서드를 호출

    • then : product의 ID 값으로 조합한 예상 productNumber가 맞는지 확인

그리고 만약 위 케이스가 아니라 특정 ID값을 연출하고 싶다면, mocking을 사용하는 방법도 있을 것 같아요.
save 메서드를 mocking하여 내가 원하는 ID 값을 가진 mock 객체로 테스트하는 방법도 있겠네요. ㅎㅎ

도움이 되셨기를 바랍니다.
감사합니다. :)

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2023.10.07

안녕하세요, 인프런 AI 인턴이에요.

ProductNumber를 계산하는 로직을 DB에서 최신값을 읽어온 후에, 그 값을 기반으로 다음 productNumber 값을 계산하는 방법에 대한 피드백을 원하시는 거죠?

일단, SaveAndFlush()로 Id를 할당받은 후에 Id 값을 기반으로 다음 productNumber 값을 계산하는 방법은 일반적으로는 문제가 없을 수 있습니다. 하지만, 동시성 이슈가 발생할 수 있는 상황에서는 문제가 될 수 있어요. 여러 개의 동시 요청이 동일한 Id 값을 받아서 계산하는 경우에는 중복된 productNumber 값이 생성될 가능성이 있기 때문입니다.

동시성 이슈에 대한 대안으로는 JPA에서 제공하는 SequenceGenerator를 사용하는 방법이 있습니다. 이 방법을 사용하면 DB에서 시퀀스를 관리하여 Id 값을 생성하므로 중복된 값을 생성하는 경우는 발생하지 않아요.

또한 calculateProductNumber() 메소드를 Entity 안에 정의하여 사용하는 방법에 대해서는 개발 방식에 따라 유연하게 선택하실 수 있습니다. 도메인 주도 설계 방식에서는 도메인 모델에 해당 로직이 포함될 수 있지만, 다른 개발 방식에서는 서비스 계층에 해당 로직을 구현하는 것도 일반적입니다. 어디에 해당 로직을 구현하느냐는 개발 팀의 규칙과 개발 패턴에 따라 달라질 수 있어요.

제가 아는 한 계산된 productNumber 값을 저장하기 전에 동시성 이슈를 고려하여 중복 여부를 확인하는 방법을 사용하는 게 좋습니다. 예를 들어, productNumber를 유니크한 필드로 설정하여 저장하기 전에 이미 해당 값이 존재하는지 확인하는 방법이 있습니다.

하나의 구체적인 상황에서 발생할 수 있는 문제에 대해서 답변을 드린 것이고, 더 자세한 내용이 있다면 추가로 질문해주세요.