ํ”„๋กœ์ ํŠธ/NolGoat

JPA ๋ฝ์œผ๋กœ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ

yongh๐Ÿ™‚ 2024. 11. 29. 02:44

๊ฐœ์š”

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

 

์ž๋ฐ”์˜ ๋™์‹œ์„ฑ ์ œ์–ด ๋ฐฉ์‹

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๋Š” ๋‹จ์ผ JVM ํ™˜๊ฒฝ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž๋ฐ” ์ˆ˜์ค€์˜ ๋™์‹œ์„ฑ ์ œ์–ด ๋ฐฉ์‹์ธ synchronized ํ‚ค์›Œ๋“œ์™€ Lock ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋จผ์ € ๊ฒ€ํ† ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

synchronized

์ž๋ฐ”์˜ ๋ชจ๋“  ๊ฐ์ฒด๋Š” ๋‚ด๋ถ€์— ๋ชจ๋‹ˆํ„ฐ ๋ฝ์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. synchronized ํ‚ค์›Œ๋“œ๋ฅผ ๋ฉ”์„œ๋“œ ํ˜น์€ ์ฝ”๋“œ ๋ธ”๋ก ๋‹จ์œ„๋กœ ์ ์šฉํ•˜๋ฉด ๋ฝ์„ ํš๋“ํ•ด์•ผ๋งŒ ํ•ด๋‹น ์˜์—ญ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•์ด ํŽธ๋ฆฌํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ, ๋ฝ ํ•ด์ œ๊นŒ์ง€ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฌดํ•œ์ • ๋Œ€๊ธฐํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ์™€ ์–ด๋–ค ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ํš๋“ํ• ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค๋Š” ๊ณต์ •์„ฑ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Lock ์ธํ„ฐํŽ˜์ด์Šค

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

 

ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹๋“ค์€ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„ ์ „์ฒด๋ฅผ ์ œ์–ดํ•˜์ง€๋Š” ๋ชปํ•ฉ๋‹ˆ๋‹ค. โ‘ ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘๊ณผ ๋ฉ”์„œ๋“œ ์ง„์ž… ์‚ฌ์ด, โ‘ก๋ฉ”์„œ๋“œ ์ข…๋ฃŒ์™€ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‚ฌ์ด์—๋Š” ์ œ์–ด๊ฐ€ ๋‹ฟ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์˜ ๋™์‹œ์„ฑ์„ ์˜จ์ „ํžˆ ๋ณด์žฅํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

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

 

ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์˜ ๋™์‹œ์„ฑ ์ œ์–ด ๋ฐฉ์‹

์•ž์„  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„๋กœ ๋™์‹œ์„ฑ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” JPA ๋ฝ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. JPA๋Š” ํฌ๊ฒŒ ๋‚™๊ด€์  ๋ฝ๊ณผ ๋น„๊ด€์  ๋ฝ ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

๋‚™๊ด€์  ๋ฝ (Optimistic Lock)

์ถฉ๋Œ์ด ๊ฑฐ์˜ ์—†์„ ๊ฒƒ์ด๋ผ ๊ฐ€์ •ํ•˜๊ณ  DB ๋ฝ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ์˜ version ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์ถฉ๋Œ์„ ๊ฐ์ง€ํ•˜์—ฌ ์ปค๋ฐ‹ ์‹œ์ ์— version ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์–ด ์žˆ์œผ๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค.

๋ณ„๋„์˜ DB ๋ฝ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ ๋น„๊ด€์  ๋ฝ๋ณด๋‹ค ์„ฑ๋Šฅ์ƒ ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์ง€๋งŒ, ์ถฉ๋Œ ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜๋ฏ€๋กœ ๋งŒ์•ฝ ์ถฉ๋Œ์ด ์žฆ์€ ์ƒํ™ฉ์ด๋ผ๋ฉด ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (+ ๋ณ„๋„์˜ ์žฌ์‹œ๋„ ๋กœ์ง๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.)

 

๋น„๊ด€์  ๋ฝ (Pessimistic Lock)

์ถฉ๋Œ์ด ์ž์ฃผ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋ผ ๊ฐ€์ •ํ•˜๊ณ  DB ๋ฝ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. SELECT ... FOR UPDATE ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์‹œ์ ๋ถ€ํ„ฐ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์˜ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

์ถฉ๋Œ์„ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ํ™•์‹คํ•˜๊ฒŒ ๋ณด์žฅํ•ด ์ฃผ์ง€๋งŒ, ๋Œ€๊ธฐ ์ƒํƒœ์˜ ํŠธ๋žœ์žญ์…˜์ด ๋Š˜์–ด๋‚˜ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ์— ๋ฝ์„ ๊ฑด ์ƒํƒœ๋กœ ์ƒํ˜ธ ๋Œ€๊ธฐํ•  ๊ฒฝ์šฐ ๋ฐ๋“œ๋ฝ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํ˜„์žฌ ์„œ๋น„์Šค๋Š” ํŠธ๋ž˜ํ”ฝ ๊ทœ๋ชจ๊ฐ€ ํฌ์ง€ ์•Š์•„ ์‹ค์ œ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํŠธ๋žœ์žญ์…˜๋งˆ๋‹ค ๋ฝ์„ ์„ ์ ํ•˜๋Š” ๋น„๊ด€์  ๋ฝ์€ ๋น„ํšจ์œจ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์—ฌ ๋‚™๊ด€์  ๋ฝ์„ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๋‚™๊ด€์  ๋ฝ ์ ์šฉ โœ…

Party ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Party extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    private Long version;
    
    ...
    
}

@Version์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฒ„์ „ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค๋‹ˆ๋‹ค.

 

Party ์„œ๋น„์Šค ํด๋ž˜์Šค

@Retryable(
    retryFor = {OptimisticLockingFailureException.class},
    backoff = @Backoff(delay = 100)
)
@Transactional
public void joinParty(String loginId, Long partyId) {
    User user = userRepository.findByLoginId(loginId)
        .orElseThrow(() -> new ApplicationException(USER_NOT_FOUND));

    // ์ด๋ฏธ ํŒŒํ‹ฐ์— ์ฐธ์—ฌ์ค‘์ธ ์œ ์ €๋Š” ์ค‘๋ณต ์‹ ์ฒญ ๋ถˆ๊ฐ€๋Šฅ
    if (partyUserRepository.existsByPartyIdAndParticipantId(partyId, user.getId())) {
        throw new ApplicationException(PARTY_ALREADY_JOINED);
    }

    Party party = partyRepository.findByIdWithFetchJoin(partyId)
        .orElseThrow(() -> new ApplicationException(PARTY_NOT_FOUND));

    // ํŒŒํ‹ฐ ์‚ญ์ œ ์—ฌ๋ถ€ ํ™•์ธ
    if (party.isDeleted()) {
        throw new ApplicationException(PARTY_ALREADY_DELETED);
    }

    // ํŒŒํ‹ฐ ๋งˆ๊ฐ ์—ฌ๋ถ€ ํ™•์ธ
    if (party.isClosed()) {
        throw new ApplicationException(PARTY_ALREADY_CLOSED);
    }

    // ํŒŒํ‹ฐ ์ƒ์„ฑ์ž๋Š” ๋ณธ์ธ์˜ ํŒŒํ‹ฐ์— ์ฐธ์—ฌ ์‹ ์ฒญ ๋ถˆ๊ฐ€๋Šฅ
    if (loginId.equals(party.getHost().getLoginId())) {
        throw new ApplicationException(PARTY_CREATOR_CANNOT_JOIN);
    }

    // ์ธ์› ์ดˆ๊ณผ ์—ฌ๋ถ€ ๊ฒ€์ฆ ๋ฐ ์ฐธ์—ฌ์ž ์ˆ˜ ์ฆ๊ฐ€
    party.incrementParticipantCount();

    // partyUser ๊ฐ์ฒด ์ €์žฅ ๋ฐ ์œ ๋‹ˆํฌ ์ œ์•ฝ ์กฐ๊ฑด ์œ„๋ฐ˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
    PartyUser partyUser = new PartyUser(party, user);
    partyUserRepository.save(partyUser);
}

@Retryable์„ ํ™œ์šฉํ•˜์—ฌ OptimisticLockingFailureException ๋ฐœ์ƒ ์‹œ ํŒŒํ‹ฐ ์ฐธ์—ฌ ๋กœ์ง์„ ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„ํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ํ˜„์žฌ ์„œ๋น„์Šค๋Š” ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, maxAttempts(์žฌ์‹œ๋„ ํšŸ์ˆ˜)์™€ multiplier(์ง€์ˆ˜ ๋ฐฑ์˜คํ”„ ์ฆ๊ฐ€ ๋น„์œจ)๋Š” ๊ธฐ๋ณธ๊ฐ’์ธ ๊ฐ๊ฐ 3ํšŒ, 0.0์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์š”์ฒญ๋Ÿ‰์ด ๋งŽ์ง€ ์•Š์•„ ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์„ ์ข€ ๋” ์งง๊ฒŒ ๊ฐ€์ ธ๊ฐ€๋„ ๋ถ€๋‹ด์ด ์—†๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ delay๋Š” 100ms๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

ํ…Œ์ŠคํŠธ

์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๊ตฌ์„ฑ

INSERT INTO users (id, login_type, login_id, password, nickname, profile_image, gender, birth_date, is_deleted)
VALUES (1, 'NORMAL', 'user1', 'password1', 'UserA', null, 'MALE', '1990-01-01', false),
       (2, 'NORMAL', 'user2', 'password2', 'UserB', null, 'FEMALE', '1992-02-02', false),
       (3, 'NORMAL', 'user3', 'password3', 'UserC', null, 'MALE', '1994-03-03', false),
       
       ...
       
       (31, 'NORMAL', 'user31', 'password31', 'UserEE', null, 'MALE', '2005-02-24', false);

INSERT INTO party (id, title, image_url, max_capacity, deadline, is_closed, host_id)
VALUES (1, 'PartyA', null, 6, '2024-12-31T23:59:59', false, 1);

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

@DisplayName("๋™์‹œ์— ์—ฌ๋Ÿฌ ํŒŒํ‹ฐ ๊ฐ€์ž… ์š”์ฒญ์ด ์™€๋„ ํŒŒํ‹ฐ์˜ ์ตœ๋Œ€ ์ธ์›์„ ์ดˆ๊ณผํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
@Test
void prevent_exceeding_max_capacity_with_concurrent_requests() throws InterruptedException {
    int threadCount = 30;
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    CountDownLatch latch = new CountDownLatch(threadCount);

    Long partyId = 1L;
    String loginIdPrefix = "user";
    for (int i = 2; i <= threadCount + 1; i++) {
        String loginId = loginIdPrefix + i;
        executorService.submit(() -> {
            try {
                partyService.joinParty(loginId, partyId);
            } catch (ApiException e) {
                System.out.println(e.getErrorCode().getMessage());
            } finally {
                latch.countDown();
            }
        });
    }
    latch.await();
    
    assertThat(partyUserRepository.countByPartyId(1L)).isEqualTo(5);
}

 

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

๐Ÿ”ด JPA ๋ฝ ๋ฏธ์ ์šฉ

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

 

๐ŸŸข JPA ๋ฝ ์ ์šฉ

  • ๋‚™๊ด€์  ๋ฝ์„ ์ ์šฉํ•˜์—ฌ ๋™์‹œ ์š”์ฒญ ์ƒํ™ฉ์—์„œ๋„ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ํŒŒํ‹ฐ์˜ ์ตœ๋Œ€ ์ธ์›์„ ์ดˆ๊ณผํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

 

๊ฒฐ๊ณผ

JPA ๋‚™๊ด€์  ๋ฝ์„ ์ ์šฉํ•˜์—ฌ ๋™์‹œ ์š”์ฒญ ์ƒํ™ฉ์—์„œ๋„ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„๋กœ ํŒŒํ‹ฐ ์ธ์›์ˆ˜ ๊ฒ€์ฆ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ฐธ์—ฌ ์ธ์›์„ ์ •ํ™•ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ด ๊ณผ์ •์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ์„ ๊ฒ€ํ† ํ•˜๊ณ , ํ˜„์žฌ ์ƒํ™ฉ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋ฐฉ์‹์„ ์„ ํƒํ•ด ์ ์šฉํ•˜๋Š” ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.