๐Ÿš€ ์ˆ˜๋…„๊ฐ„ ๋ฌด์‹œํ–ˆ๋˜ jpa ๋ฉ”์„œ๋“œ๋“ค โ€” ์ด๊ฒƒ๋งŒ ์•Œ๋ฉด spring boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ 10๋ฐฐ ๋นจ๋ผ์ง„๋‹ค!

ยท5 min readยท2ยท
๐Ÿš€ ์ˆ˜๋…„๊ฐ„ ๋ฌด์‹œํ–ˆ๋˜ JPA ๋ฉ”์„œ๋“œ๋“ค โ€” ์ด๊ฒƒ๋งŒ ์•Œ๋ฉด Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ 10๋ฐฐ ๋นจ๋ผ์ง„๋‹ค!

๐Ÿš€ ์ˆ˜๋…„๊ฐ„ ๋ฌด์‹œํ–ˆ๋˜ JPA ๋ฉ”์„œ๋“œ๋“ค โ€” ์ด๊ฒƒ๋งŒ ์•Œ๋ฉด Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ 10๋ฐฐ ๋นจ๋ผ์ง„๋‹ค!

์ˆ˜๋…„๊ฐ„ ๋ฌด์‹œํ•ด์™”๋˜ ํ•ต์‹ฌ JPA ๋ฉ”์„œ๋“œ์™€ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์„ ๋งˆ์นจ๋‚ด ํƒ๊ตฌํ•ด๋ณด๋‹ˆ, ๋†€๋ผ์šด ์ผ์ด ๋ฒŒ์–ด์กŒ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด 10๋ฐฐ ๋” ๋นจ๋ผ์ง€๊ณ , ์ฟผ๋ฆฌ ์ˆ˜๋Š” ์ค„์–ด๋“ค๊ณ , ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๋„ ๊ฐ์†Œํ–ˆ์œผ๋ฉฐ, ์ฝ”๋“œ๋„ ํ›จ์”ฌ ๊น”๋”ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.


Gemini Generated Image tk480ltk480ltk48 (1) (1)

๐Ÿ”„ 1. existsById() โ€” findById() ๋Œ€์‹  ์‚ฌ์šฉํ•˜๊ธฐ

๋‹จ์ˆœํžˆ ์กด์žฌ ์—ฌ๋ถ€๋งŒ ํ™•์ธํ•˜๋ฉด ๋  ๋•Œ:

if (userRepository.existsById(id)) {
    // ์ „์ฒด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋กœ๋“œํ•  ํ•„์š” ์—†์Œ
}

โœ… ์™œ ๋” ๋น ๋ฅธ๊ฐ€?

  • ๋ถˆํ•„์š”ํ•œ ์—”ํ‹ฐํ‹ฐ ๋กœ๋”ฉ์„ ํ”ผํ•จ
  • SQL ํฌ๊ธฐ ๊ฐ์†Œ
  • ๋‹จ์ˆœํ•œ SELECT COUNT(*) ๋˜๋Š” SELECT 1 ์ฟผ๋ฆฌ๋งŒ ์‹คํ–‰

๐Ÿ’พ 2. saveAll() โ€” ์•”๋ฌต์  ๋ฐฐ์น˜ ์ €์žฅ

์ˆ˜๋…„๊ฐ„ ์ด๋ ‡๊ฒŒ ์ €์žฅํ–ˆ๋‹ค๋ฉด...

// โŒ ๋น„ํšจ์œจ์ ์ธ ๋ฐฉ์‹
for (User user : users) {
    userRepository.save(user);
}

์ด์ œ ์ด๋ ‡๊ฒŒ ๋ฐ”๊พธ์„ธ์š”:

// โœ… ํšจ์œจ์ ์ธ ๋ฐฉ์‹
userRepository.saveAll(users);

๐Ÿ’ก ํžŒํŠธ

saveAll()์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๊ฐ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•ด save()๋ฅผ ํ˜ธ์ถœํ•˜์ง€๋งŒ, hibernate.jdbc.batch_size๋ฅผ ์„ค์ •ํ•˜๋ฉด persistence context๊ฐ€ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โš™๏ธ 3. saveAndFlush() โ€” ์ฆ‰์‹œ ์˜์†ํ™”

๐Ÿง  ๋ฌธ์ œ ์ƒํ™ฉ

๋ ˆ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์ฆ‰์‹œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ DB์— ์žˆ์–ด์•ผ ํ•˜๋Š” ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ๋•Œ (๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ, ํŠธ๋ฆฌ๊ฑฐ, ๋‹ค๋ฅธ ์„œ๋น„์Šค์—์„œ DB ์กฐํšŒ ๋“ฑ)

// โŒ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ
User newUser = userRepository.save(user);
auditRepository.save(new AuditLog("User created: " + newUser.getId()));

save()๋Š” ์•„์ง ์‹ค์ œ๋กœ INSERT๋ฅผ flushํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, audit ์„œ๋น„์Šค๊ฐ€ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์˜์†ํ™”๋œ user์— ์˜์กดํ•œ๋‹ค๋ฉด ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก ํ•ด๊ฒฐ์ฑ… โ€” saveAndFlush() ์‚ฌ์šฉ

// โœ… ์ฆ‰์‹œ ์˜์†ํ™”
User newUser = userRepository.saveAndFlush(user);
auditRepository.save(new AuditLog("User created: " + newUser.getId()));

โœ… ๋™์ž‘ ๋ฐฉ์‹

saveAndFlush()๋Š” ๋‘ ๊ฐ€์ง€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

  1. ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†ํ™” (save()์ฒ˜๋Ÿผ)
  2. ๋‚ด๋ถ€์ ์œผ๋กœ EntityManager.flush()๋ฅผ ์ฆ‰์‹œ ํ˜ธ์ถœ

์ƒˆ ๋ ˆ์ฝ”๋“œ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ”๋กœ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.


๐Ÿชถ 4. getReferenceById() โ€” ๊ฒฝ๋Ÿ‰ ์—”ํ‹ฐํ‹ฐ ๋กœ๋”

โš™๏ธ ๋™์ž‘ ์›๋ฆฌ

getReferenceById()๋Š” EntityManager.getReference()์˜ Spring Data JPA ๋ž˜ํผ์ž…๋‹ˆ๋‹ค.

User userRef = userRepository.getReferenceById(userId);
order.setUser(userRef);
orderRepository.save(order);

โœ… ํ•ต์‹ฌ ํฌ์ธํŠธ

  • SQL ์ฟผ๋ฆฌ๊ฐ€ ์ฆ‰์‹œ ์‹คํ–‰๋˜์ง€ ์•Š์Œ
  • JPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ ์ง€์—ฐ ํ”„๋ก์‹œ(lazy proxy) ์ƒ์„ฑ
  • ID ์™ธ์˜ ๋‹ค๋ฅธ ํ•„๋“œ์— ์ ‘๊ทผํ•  ๋•Œ๋งŒ Hibernate๊ฐ€ DB ์กฐํšŒ

๐Ÿ“‰ ์™œ ๋” ๋น ๋ฅธ๊ฐ€?

๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ์—์„œ ์™ธ๋ž˜ ํ‚ค๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐธ์กฐ๋งŒ ํ•˜๋ฉด ๋˜๋Š” ๊ฒฝ์šฐ, ์ „์ฒด ๊ฐ์ฒด๋ฅผ DB์—์„œ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์€ ์‹œ๊ฐ„ ๋‚ญ๋น„์ž…๋‹ˆ๋‹ค.

// โœ… ์ตœ์ ํ™”๋œ ๋ฐฉ์‹
User userRef = userRepository.getReferenceById(userId);
order.setUser(userRef);

SELECT ์ฟผ๋ฆฌ ์—†์Œ, ๋ฐ์ดํ„ฐ ํŽ˜์นญ ์—†์Œ, ๋‹จ์ง€ ์ฐธ์กฐ๋งŒ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.


๐Ÿงน 5. deleteById() โ€” ๊น”๋”ํ•œ ์‚ญ์ œ

์ด๋ ‡๊ฒŒ ์‚ญ์ œํ–ˆ๋‹ค๋ฉด...

// โŒ ๋น„ํšจ์œจ์ ์ธ ๋ฐฉ์‹
User user = userRepository.findById(id).orElseThrow();
userRepository.delete(user);

์ด๊ฒƒ์€ ๊ฐ€์žฅ ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์ด ์•„๋‹™๋‹ˆ๋‹ค.

โš™๏ธ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•

// โœ… ํšจ์œจ์ ์ธ ๋ฐฉ์‹
userRepository.deleteById(userId);

โœ… ๊ฒฐ๊ณผ

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋จผ์ € ๊ฐ€์ ธ์˜ค๋Š” SELECT ์ฟผ๋ฆฌ ์—†์Œ
  • Spring Data JPA๊ฐ€ ์ฃผ์–ด์ง„ ID์— ๋Œ€ํ•ด ์ง์ ‘ DELETE FROM ๋ฌธ ์‹คํ–‰

๐Ÿ“‰ ์™œ ๋” ๋น ๋ฅธ๊ฐ€?

delete(entity)๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด Hibernate๋Š” ๋ณดํ†ต ๋‘ ๋‹จ๊ณ„๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

  1. ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•œ SELECT ์ฟผ๋ฆฌ ์‹คํ–‰
  2. ์‚ญ์ œ๋ฅผ ์œ„ํ•œ DELETE ๋ฌธ ์‹คํ–‰

deleteById()๋Š” ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค!


๐Ÿ”„ 6. flush() โ€” ๋™๊ธฐํ™” ๋„๊ตฌ

์–ธ๋œป ๋ณด๋ฉด JPA์˜ flush()๊ฐ€ ์‹ ๋น„๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. save()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ๊ฐ€ DB์— ๋‚˜ํƒ€๋‚˜๊ณ  ๋ชจ๋“  ๊ฒƒ์ด "๊ทธ๋ƒฅ ์ž‘๋™"ํ•˜๋‹ˆ๊นŒ์š”.

โš™๏ธ flush()๊ฐ€ ์‹ค์ œ๋กœ ํ•˜๋Š” ์ผ

save()๋‚˜ persist()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ์—”ํ‹ฐํ‹ฐ๋Š” ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ์ €์žฅ๋˜๋ฉฐ ์ฆ‰์‹œ DB์— ๊ธฐ๋ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

flush()๋Š” Hibernate์—๊ฒŒ ์ธ๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ๋ฅผ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๊ฐ•์ œ๋กœ ๋™๊ธฐํ™”ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ์š”์•ฝ

๐Ÿ’ก flush() = "๋Œ€๊ธฐ ์ค‘์ธ ๋ชจ๋“  SQL ๋ณ€๊ฒฝ ์‚ฌํ•ญ(INSERT, UPDATE, DELETE)์„ ์ง€๊ธˆ ๋‹น์žฅ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๋ณด๋‚ด๋ผ"


๐Ÿ“ฆ 7. findAll(Sort) / findAll(Pageable) โ€” ์ •๋ ฌ๊ณผ ํŽ˜์ด์ง€๋„ค์ด์…˜

๋ชฉ์ 

์ •๋ ฌ๊ณผ ํŽ˜์ด์ง€๋„ค์ด์…˜์œผ๋กœ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ

๐Ÿ’ช ์™œ ๊ฐ•๋ ฅํ•œ๊ฐ€?

ํ•„์š”ํ•œ ์Šฌ๋ผ์ด์Šค๋งŒ ๋กœ๋“œํ•˜์—ฌ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์กฐํšŒ๋ฅผ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.

Page<User> users = userRepository.findAll(
    PageRequest.of(0, 20, Sort.by("name"))
);

โœ… ์žฅ์ 

  • ๋Œ€์šฉ๋Ÿ‰ ์ฟผ๋ฆฌ๋กœ ์ธํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ณผ๋ถ€ํ•˜ ๋ฐฉ์ง€
  • ๋‚ด์žฅ๋œ ํŽ˜์ด์ง€๋„ค์ด์…˜์ด Spring Data์—์„œ ์ž๋™ ์ฒ˜๋ฆฌ

๐Ÿงฎ 8. count() โ€” ๋น ๋ฅธ ์นด์šดํŠธ

๋ชฉ์ 

๋ ˆ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ์ˆ˜๋งŒ ์„ธ๊ธฐ

๐Ÿ’ช ์™œ ๊ฐ•๋ ฅํ•œ๊ฐ€?

์ตœ์ ํ™”๋œ SQL COUNT(*) ์‹คํ–‰

long activeUsers = userRepository.countByStatus("ACTIVE");

โœ… ํ™œ์šฉ์ฒ˜

๋Œ€์‹œ๋ณด๋“œ, ์š”์•ฝ ์ •๋ณด, ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ด ๊ฐœ์ˆ˜์— ์ด์ƒ์ 


๐Ÿงฉ 9. JPA ํŒŒ์ƒ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ โ€” ๋ณตํ•ฉ ํ‚ค ๊ฒ€์ƒ‰

Spring Data JPA์˜ ํŒŒ์ƒ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๋น›์„ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

โš™๏ธ ๋™์ž‘ ์›๋ฆฌ

Spring Data JPA๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ž์ฒด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ SQL ์ฟผ๋ฆฌ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ˆ˜๋™ @Query ์—†์Œ, JPQL ์—†์Œ, ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์—†์Œ!

Optional<Client> findByClientIdAndCustomerNoAndCountryCode(
    String clientId,
    String customerNo,
    String countryCode
);

Spring Data๊ฐ€ ์ด๊ฒƒ์„ ํ•ด์„ํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

SELECT * FROM client
WHERE client_id = ?
AND customer_no = ?
AND country_code = ?;

โœ… ์žฅ์ 

  • JPQL์ด๋‚˜ ๋„ค์ดํ‹ฐ๋ธŒ SQL ํ•„์š” ์—†์Œ
  • ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ ์—†์Œ
  • ๋†’์€ ๊ฐ€๋…์„ฑ๊ณผ ํƒ€์ž… ์•ˆ์ „์„ฑ

โšก ๋งˆ์ง€๋ง‰ ํŒ

๐Ÿ‘‰ JPA๋ฅผ ๋ธ”๋ž™๋ฐ•์Šค๋กœ ์ทจ๊ธ‰ํ•˜์ง€ ๋งˆ์„ธ์š”. ์ •๊ตํ•˜๊ฒŒ ํŠœ๋‹๋œ ์—”์ง„์ฒ˜๋Ÿผ ๋‹ค๋ฃจ์„ธ์š”. ๊ทธ๋ฆฌ๊ณ  ๋‹น์‹ ์€ ์–ธ์ œ ๊ฐ€์†ํ•˜๊ณ , ์–ธ์ œ ๊ธฐ์–ด๋ฅผ ๋ฐ”๊พธ๊ณ , ์–ธ์ œ ๊ด€์„ฑ ์ฃผํ–‰ํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋“œ๋ผ์ด๋ฒ„์ž…๋‹ˆ๋‹ค.

์ด ๋ฆฌ๋“ฌ์„ ์ตํžˆ๋ฉด, JPA๋Š” Spring Boot ํˆดํ‚ท์—์„œ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๐Ÿ’ช๐Ÿš€


๐Ÿ“‹ ๋ฉ”์„œ๋“œ ์š”์•ฝ ํ‘œ

๋ฉ”์„œ๋“œ์šฉ๋„์„ฑ๋Šฅ ์ด์ 
existsById()์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ์—”ํ‹ฐํ‹ฐ ๋กœ๋”ฉ ๋ถˆํ•„์š”
saveAll()๋ฐฐ์น˜ ์ €์žฅ๋ฐฐ์น˜ ์ตœ์ ํ™” ๊ฐ€๋Šฅ
saveAndFlush()์ฆ‰์‹œ ์˜์†ํ™”DB ์ฆ‰์‹œ ๋ฐ˜์˜
getReferenceById()์ฐธ์กฐ๋งŒ ํ•„์š”ํ•  ๋•ŒSELECT ์ฟผ๋ฆฌ ์—†์Œ
deleteById()ID๋กœ ์‚ญ์ œSELECT ์—†์ด ์ง์ ‘ DELETE
flush()๊ฐ•์ œ ๋™๊ธฐํ™”์ฆ‰์‹œ DB ๋ฐ˜์˜
findAll(Pageable)ํŽ˜์ด์ง€๋„ค์ด์…˜ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋กœ๋“œ
count()๊ฐœ์ˆ˜ ์กฐํšŒ์ตœ์ ํ™”๋œ COUNT ์ฟผ๋ฆฌ
ํŒŒ์ƒ ์ฟผ๋ฆฌ์กฐ๊ฑด๋ถ€ ๊ฒ€์ƒ‰์ž๋™ ์ฟผ๋ฆฌ ์ƒ์„ฑ

๐Ÿ”— ์ถœ์ฒ˜