๋™์‹œ์„ฑ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ณ ๋ฏผ๊ณผ ํ•ด๊ฒฐ ๊ณผ์ •

1. ๊ฐœ์š”

ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ ์ฐฝ์—… ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์ด ๋Œ€๋ถ€๋ถ„ ๋๋‚˜๊ณ , ์‚ฌ์—…์ž๊ฐ€ ๋‚˜์™€์•ผ ํ•  ์ˆ˜ ์žˆ๋Š” SMS, ์•Œ๋ฆผํ†ก ๋ถ€๋ถ„๊ณผ ๋ฑ…ํ‚น ๊ธฐ๋Šฅ๋งŒ์ด ๋‚จ์•˜๋‹ค. 

 

์ง€๋‚œ 5๊ฐœ์›”๊ฐ„ ์ •์‹ ์—†์ด ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์— ์ง‘์ค‘ํ•˜๋‹ค๋ณด๋‹ˆ ๋ฏธํกํ•œ ๋ถ€๋ถ„์ด ๊ฝค๋‚˜ ๋ณด์˜€๊ณ  ์ตœ๊ทผ์—” ํ•˜๋‚˜ํ•˜๋‚˜ ๋ฆฌํŽ™ํ„ฐ๋ง ๊ณผ์ •์„ ๋ฐŸ๊ณ ์žˆ๋‹ค. 

 

๊ผผ๊ผผํžˆ ์ง€๋‚œ ์ฝ”๋“œ๋ฅผ ์‚ดํ”ผ๋˜ ์ค‘ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ๋งŒํ•œ ๋กœ์ง์ด ๋ฐœ๊ฒฌ๋๊ณ  ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž Thread-Safe, ๋™์‹œ์„ฑ ๋ฌธ์ œ์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๊ฒŒ ๋๋‹ค.

 

์•„๋ž˜ ๊ฒŒ์‹œ๊ธ€์—์„œ ๊ณต๋ถ€ํ–ˆ๋˜ ๋‚ด์šฉ๋“ค์„ ์ •๋ฆฌํ•ด๋’€๋‹ค. 

 

https://dgjinsu.tistory.com/53

 

๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ์‚ฌ์šฉ ์‹œ ๊ผญ ์•Œ์•„์•ผ ํ•  ๋‚ด์šฉ - Thread-Safe

๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฐ€์žฅ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ž์›์„ ๊ณต์œ ํ•œ๋‹ค๋ฉด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. Java์—์„  ์–ด๋–ป๊ฒŒ Thread-Safeํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜

dgjinsu.tistory.com

 

https://dgjinsu.tistory.com/54

 

Spring Boot ์˜ ๋‹ค์ค‘ ์œ ์ € ์š”์ฒญ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ• (Tomcat)

1. Thread ์Šค๋ ˆ๋“œ์˜ ๊ฐœ๋…์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์‹คํ–‰์ค‘์ธ ํ•œ ํ”„๋กœ๊ทธ๋žจ(ํ”„๋กœ์„ธ์Šค) ๋‚ด์—์„œ ๊ตฌ๋ถ„์ง€์–ด์ง„ ์‹คํ–‰ ๋‹จ์œ„ ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค์—์„œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋‹จ์œ„๋กœ ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ์‹ค์ƒํ™œ ์˜ˆ๋ฅผ ๋“ค๋ฉด, ์€ํ–‰

dgjinsu.tistory.com

 

ํ•˜์ง€๋งŒ ๋‚ด ์ฝ”๋“œ์˜ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋กœ ๋‹ค๋ฃจ๋Š” ์ƒํ™ฉ๋“ค๊ณผ๋Š” ์กฐ๊ธˆ์€ ๋‹ฌ๋ž๋‹ค. 

 

 

 

 

 

 

 

 

2. ๋ฌธ์ œ ์ƒํ™ฉ

์œ„ ์‚ฌ์ง„์€ ๋‚ด ์ฝ”๋“œ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์„ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋„๋ก ๊ทธ๋ฆฐ ์‚ฌ์ง„์ด๋‹ค. 

 

๋ฌธ์ œ ์ƒํ™ฉ์„ ๊ธ€๋กœ๋„ ํ‘œํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

๊ฐœ๋ฐœ ์š”๊ตฌ ์‚ฌํ•ญ

๊ฐ™์€ ๋‚ ์งœ์— ๋…ธ๋™์ž๋Š” ๋‘ ๊ฐœ์˜ ์ผ์„ ํ•  ์ˆ˜ ์—†๋‹ค. ์ฆ‰, ํ•œ ๊ฐœ ์ด์ƒ์˜ status ๊ฐ€ accepted์ธ Apply(์ง€์›) ์ด ์žˆ์œผ๋ฉด ์•ˆ ๋จ.

 

๋…ธ๋™์ž๋Š” ์ผ์ž๋ฆฌ ๊ณต๊ณ ์— ์ง€์›(Apply)ํ•  ์ˆ˜ ์žˆ๊ณ  ๊ธฐ์—…์ด ํ•ด๋‹น ๋…ธ๋™์ž์˜ ์ง€์›(Apply)๋ฅผ ์ˆ˜๋ฝํ•ด์•ผ ํ™•์ •์ด ๋œ๋‹ค. ์ˆ˜๋ฝ ์ „์˜ ์ง€์›(Apply)์˜ status๋Š” pending์ด๋‹ค. 

์ˆ˜๋ฝ์„ ํ•˜๋ฉด ์ง€์›(Apply)์˜ status ํ•„๋“œ๋Š” accepted๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.

 

๋…ธ๋™์ž๋Š” ๊ธฐ์—…์œผ๋กœ๋ถ€ํ„ฐ ์ผ์ž๋ฆฌ๋ฅผ ์ œ์•ˆ(Offer) ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ์ œ์•ˆ(Offer)์€ ๋…ธ๋™์ž๊ฐ€ ์ˆ˜๋ฝํ•˜๋ฉด ๋ฐ”๋กœ ์ผ์ž๋ฆฌ๊ฐ€ ํ™•์ •๋œ๋‹ค. ๋…ธ๋™์ž๊ฐ€ ์ œ์•ˆ(Offer)๋ฅผ ์ˆ˜๋ฝํ•˜๋ฉด status ๊ฐ€ accepted์ธ Apply(์ง€์›) ์„ ์ƒ์„ฑํ•œ๋‹ค.

 

 

๊ทธ๋ ‡๋‹ค๋ฉด ๊ธฐ์—…์ด ๋…ธ๋™์žA์˜ ์ง€์›์„ ์ˆ˜๋ฝํ•˜๋Š” ๋™์‹œ์—, ๋…ธ๋™์žA๊ฐ€ ์ œ์•ˆ๋ฐ›์€ ์ผ์ž๋ฆฌ๋ฅผ ์ˆ˜๋ฝํ•œ๋‹ค๋ฉด status ๊ฐ€ accepted์ธ Apply(์ง€์›) ์ด ๋‘ ๊ฐœ ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค. 

 

๋ฐ”๋กœ ์—ฌ๊ธฐ์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

 

๊ทธ๋ ‡๋‹ค๋ฉด ์œ„์—์„œ ์ผ๋ฐ˜์ ์ธ ๋™์‹œ์„ฑ ์ƒํ™ฉ๊ณผ๋Š” ๋‹ค๋ฅด๋‹ค๊ณ  ํ•œ ์ด์œ ๋Š” ๋ญ˜๊นŒ?

 

์ผ๋ฐ˜์ ์ธ ๋™์‹œ์„ฑ ๋ฌธ์ œ์—์„  ํ•˜๋‚˜์˜ ๊ณต์œ  ์ž์›์„ ์ˆ˜์ •ํ•  ๋•Œ๋ฅผ ๋งŽ์ด ๋‹ค๋ฃฌ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋ฝ์„ ๊ฑธ์–ด ํ•ด๊ฒฐํ•˜์ง€๋งŒ, ๋‚˜๋Š” Apply(์ง€์›)์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฝ์„ ๊ฑธ๊ธฐ์— ์• ๋งคํ–ˆ๋‹ค. 

 

 

 

 

 

 

 

 

 

3. ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

์ฒ˜์Œ ๋– ์˜ฌ๋ฆฐ ๋ฐฉ์•ˆ์€ ๋ฉ”์‹ ์ € ํ ๊ฐ™์€ ํ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‘ ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๋ฐฉ์•ˆ์„ ๋– ์˜ฌ๋ ธ๋‹ค. 

 

์นดํ”„์นด ๊ฐ™์€ ๊ธฐ์ˆ ์„ ๋„์ž…ํ•˜๊ธฐ์—” ์•„์ง ๋ถ€๋‹ด์Šค๋Ÿฌ์› ๊ณ , ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ๋‹ค ๋ฝ์„ ๊ฑธ ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์œผ๋กœ ๋กœ์ง ์„ค๊ณ„๋ฅผ ๋ฐ”๊ฟ”๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค. 

 

Apply(์ง€์›) ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฝ์ด ๊ฑธ๊ธฐ ํž˜๋“ค์—ˆ์œผ๋‹ˆ, Offer(์ œ์•ˆ)ํ•  ๋•Œ Apply(์ง€์›) ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๋ฏธ๋ฆฌ ๋„ฃ์–ด๋‘๋ฉด ๋ฝ์„ ๊ฑธ์–ด ๋™์‹œ์„ฑ์„ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์„๊ฑฐ๋ผ ์ƒ๊ฐํ–ˆ๋‹ค. 

 

 

 

๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.

public void processOffer(Long workerId, OfferProcessRequest request) {
    // ์ƒ๋žต

    // ์š”์ฒญ์— ๋”ฐ๋ผ Apply ์‚ญ์ œ or status ์—…๋ฐ์ดํŠธ
    Apply offeredApply = applyRepository.findOfferedApply(worker.getId(), workDate.getId())
            .orElseThrow(() -> new CustomException(ErrorCode.APPLY_OFFERED_NOT_FOUND));

    // ์ˆ˜๋ฝ์ผ ๋•Œ
    if (request.getIsAccept()) {
        // ์ƒ๋žต
        
        // ๋ชจ์ง‘๋œ ์ธ์› ์ฆ๊ฐ€
        workDate.plusRegisteredNum(1);

        // ์ œ์•ˆ ๋ฐ›์œผ๋ฉฐ ์ž๋™์œผ๋กœ ์ƒ๊ธด ์ง€์› ๊ธฐ๋ก ์Šน์ธ ์ฒ˜๋ฆฌ
        offeredApply.updateStatus(ApplyStatus.ACCEPTED, LocalDateTime.now());

        //์ƒ๋žต
    }
    // ๊ฑฐ์ ˆ์ผ ๋•Œ
    else {
        // ์ œ์•ˆ ๋ฐ›์œผ๋ฉฐ ์ž๋™์œผ๋กœ ์ƒ๊ธด ์ง€์› ๊ธฐ๋ก ์ œ๊ฑฐ
        applyRepository.delete(offeredApply);
    }

    // ์ƒ๋žต
}

 

์œ„ ์ฝ”๋“œ์—์„œ ์กฐํšŒํ•œ offerdApply์˜ status๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ์ค‘, ๊ธฐ์—…์ด ๋‹ค๋ฅธ Apply(์ œ์•ˆ)์„ ๊ธฐ์—…์ด ์ˆ˜๋ฝํ•ด๋ฒ„๋ฆฌ๋ฉด ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.  

 

ํ•ด๊ฒฐ์„ ์œ„ํ•ด ๋น„๊ด€์  Lock์„ ์‚ฌ์šฉํ•˜์—ฌ member์˜ Apply ๋ฐ์ดํ„ฐ ์ „๋ถ€์— Lock์„ ๊ฑธ์–ด์ฃผ์—ˆ๋‹ค. 

public void processOffer(Long workerId, OfferProcessRequest request) {
    // ์ƒ๋žต

    // ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ Lock ์ฟผ๋ฆฌ
    applyRepository.findWorkerApplyForLock(worker.getId());

    // ์š”์ฒญ์— ๋”ฐ๋ผ Apply ์‚ญ์ œ or status ์—…๋ฐ์ดํŠธ
    Apply offeredApply = applyRepository.findOfferedApply(worker.getId(), workDate.getId())
            .orElseThrow(() -> new CustomException(ErrorCode.APPLY_OFFERED_NOT_FOUND));

    // ์ˆ˜๋ฝ์ผ ๋•Œ
    if (request.getIsAccept()) {
        // ์ƒ๋žต
        
        // ๋ชจ์ง‘๋œ ์ธ์› ์ฆ๊ฐ€
        workDate.plusRegisteredNum(1);

        // ์ œ์•ˆ ๋ฐ›์œผ๋ฉฐ ์ž๋™์œผ๋กœ ์ƒ๊ธด ์ง€์› ๊ธฐ๋ก ์Šน์ธ ์ฒ˜๋ฆฌ
        offeredApply.updateStatus(ApplyStatus.ACCEPTED, LocalDateTime.now());

        //์ƒ๋žต
    }
    // ๊ฑฐ์ ˆ์ผ ๋•Œ
    else {
        // ์ œ์•ˆ ๋ฐ›์œผ๋ฉฐ ์ž๋™์œผ๋กœ ์ƒ๊ธด ์ง€์› ๊ธฐ๋ก ์ œ๊ฑฐ
        applyRepository.delete(offeredApply);
    }

    // ์ƒ๋žต
}

 

 

 

Repository ์ฝ”๋“œ์ด๋‹ค.

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select a from Apply a where a.member.id = :memberId")
List<Apply> findWorkerApplyForLock(@Param("memberId") Long memberId);

 

 

์œ„์™€ ๊ฐ™์ด @Lock์„ ๋ถ™์—ฌ์คŒ์œผ๋กœ์จ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ„๋‹ค.

 

๋น„๊ด€์  Lock์„ ๊ฑด ๊ฒฝ์šฐ select ~ for update ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ„๋‹ค. ์ฆ‰, ์—ฌ๊ธฐ์„œ ์กฐํšŒ๋œ ๋ ˆ์ฝ”๋“œ๋Š” Lock์ด ๊ฑธ๋ฆฐ๋‹ค. 

 

 

 

 

 

 

 

4. ๊ฒฐ๋ก 

๋™์‹œ์„ฑ ๋ฌธ์ œ๋Š” ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์กฐ์‹ฌํ•ด์„œ ๊ฐœ๋ฐœํ•ด์•ผ ํ•œ๋‹ค. ๋‚˜๊ฐ™์€ ๊ฒฝ์šฐ์—๋„ ์ง€์› ํ™•์ •์ด ๋‘ ๊ฐœ ๋‚˜๋ฒ„๋ฆฌ๋ฉด ๊ธฐ์—… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋…ธ๋™์ž์—๊ฒŒ๋„ ํƒ€๊ฒฉ์ด ๊ฐ€๋Š” ์ƒํ™ฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋“œ๋ฌผ์ง€๋งŒ ๊ทธ๋Ÿฐ ์ผ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋„๋ก ๋ฐฉ์ง€ํ•ด์•ผํ•œ๋‹ค.

 

 

์œ„์—์„œ ๋‹ค๋ฃฌ ์ƒํ™ฉ ๋ง๊ณ ๋„ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์— ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๋กœ์ง๋“ค์ด ๋” ์กด์žฌํ•œ๋‹ค. ํ˜„์—…์—์„  ์ด๋ณด๋‹ค ์ˆ˜๋งŽ์€ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๋“ค์ด ๋งŽ์ด ๋ฐœ์ƒํ• ํ…๋ฐ ๋นจ๋ฆฌ ๊ฒฝํ—˜ํ•ด๋ณด๊ณ ์‹ถ๋‹ค..ใ…Ž