๊ด€๋ฆฌ ๋ฉ”๋‰ด

์‚ถ์˜ ๊ณต์œ 

[Spring] ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ! — JPA์—์„œ SQL ์—†์ด ์กฐํšŒํ•˜๊ธฐ ๋ณธ๋ฌธ

Web Dev/BackEnd

[Spring] ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ! — JPA์—์„œ SQL ์—†์ด ์กฐํšŒํ•˜๊ธฐ

dkrehd 2025. 5. 9. 03:50
728x90
๋ฐ˜์‘ํ˜•

 

๐Ÿง™‍โ™‚๏ธ ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ! — JPA์—์„œ SQL ์—†์ด ์กฐํšŒํ•˜๊ธฐ

์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ SQL ํ•œ ์ค„๋„ ์•ˆ ์“ฐ๊ณ ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„์ฃผ๋Š”, ์ •๋ง ์‹ ๊ธฐํ•œ ๊ธฐ๋Šฅ
**Spring Data JPA์˜ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ(Query Method)**์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค ๐Ÿ˜Š


๐Ÿง  ๋จผ์ €, JPA๊ฐ€ ์ง€์›ํ•˜๋Š” ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ ๋ฐฉ์‹!

JPA์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์–ด์š”. ์•„๋ž˜์ฒ˜๋Ÿผ์š”:

๋ฐฉ์‹์„ค๋ช…
JPQL JPA ์ „์šฉ SQL (Entity ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ, SQL๊ณผ ์œ ์‚ฌ)
Query Method ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ ์ž˜ ์ง€์œผ๋ฉด ์ž๋™์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ๋Šฅ
Criteria ์ฝ”๋“œ ์กฐํ•ฉ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” JPA ํ‘œ์ค€ ๋ฐฉ์‹ (ํ•˜์ง€๋งŒ ๋ถˆํŽธํ•ด์š”!)
Querydsl Criteria๋ณด๋‹ค ์‰ฝ๊ณ  ๊ฐ•๋ ฅํ•œ ์ฟผ๋ฆฌ ์ž‘์„ฑ ๋„๊ตฌ (์˜คํ”ˆ์†Œ์Šค)
Native SQL ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ SQL์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ์‹ (๋ณต์žกํ•œ ์ฟผ๋ฆฌ์— ์‚ฌ์šฉ)

 

๐Ÿง™‍โ™€๏ธ ๊ทธ์ค‘์—์„œ๋„ ์˜ค๋Š˜์˜ ์ฃผ์ธ๊ณต! — ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ(Query Method)

Spring Data JPA์—์„œ๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ ์ž˜ ์ง€์œผ๋ฉด,
์ž๋™์œผ๋กœ SELECT, DELETE, COUNT ๊ฐ™์€ SQL์„ ๋งŒ๋“ค์–ด์ค˜์š”!

 

์˜ˆ์‹œ:

List<Board> list = boardRepository.findByTitleAndWriter("์ œ๋ชฉ", "๊ธ€์“ด์ด");

์ด ํ•œ ์ค„์ด ์‚ฌ์‹ค์€ SQL์„ ๋Œ€์‹ ํ•˜๊ณ  ์žˆ์–ด์š”!

SELECT * FROM board WHERE title = '์ œ๋ชฉ' AND writer = '๊ธ€์“ด์ด';
 
 

์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์™œ ์ข‹์„๊นŒ? 

  • ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ: ๋ณต์žกํ•œ SQL/JPQL์„ ์ง์ ‘ ์ž‘์„ฑํ•  ํ•„์š” ์—†์ด ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ ๊ธฐ๋ณธ์ ์ธ ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•ด ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค.
  • ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ: ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ž์ฒด๊ฐ€ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ค ์กฐ๊ฑด์œผ๋กœ ์ฐพ๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณด์—ฌ์ฃผ๋ฏ€๋กœ, ์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ์‚ฌ๋žŒ์ด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด: ์กฐํšŒ ์กฐ๊ฑด์ด ๋ณ€๊ฒฝ๋  ๋•Œ, ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ ๊ทœ์น™์— ๋งž๊ฒŒ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ํƒ€์ž… ์•ˆ์ •์„ฑ: ์ปดํŒŒ์ผ ์‹œ์ ์— ๋ฉ”์„œ๋“œ ์ด๋ฆ„๊ณผ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž… ์˜ค๋ฅ˜๋ฅผ ์žก์„ ์ˆ˜ ์žˆ์–ด ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๊ฐ€๋Šฅ์„ฑ์„ ์ค„์—ฌ์ค๋‹ˆ๋‹ค.
  • ํ”„๋ ˆ์ž„์›Œํฌ ํ™œ์šฉ: Spring Data JPA์™€ ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ๋ณ„๋„์˜ ์„ค์ • ์—†์ด ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 
 
 

๐Ÿงฑ BoardRepository ํด๋ž˜์Šค ์‚ดํŽด๋ณด๊ธฐ

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ Repository์— ์ •์˜๋˜๋Š”์ง€ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค!

public interface BoardRepository extends CrudRepository<Board, Long> {

    // 1. "์ž‘์„ฑ์ž ์ด๋ฆ„์ด [writer]์ธ ๊ธ€์ด ์ด ๋ช‡ ๊ฐœ์ธ์ง€ ์„ธ์–ด์ฃผ์„ธ์š”!"
    int countAllByWriter(String writer);

    // 2. "์ž‘์„ฑ์ž ์ด๋ฆ„์ด [writer]์ธ ๊ธ€๋“ค์„ ๋ชจ๋‘ ์ฐพ์•„์ฃผ์„ธ์š”!"
    List<Board> findByWriter(String writer);

    // 3. "์ œ๋ชฉ์ด [title]์ด๊ณ , ์ž‘์„ฑ์ž ์ด๋ฆ„์ด [writer]์ธ ๊ธ€๋“ค์„ ๋ชจ๋‘ ์ฐพ์•„์ฃผ์„ธ์š”!"
    List<Board> findByTitleAndWriter(String title, String writer);

    // 4. "์ž‘์„ฑ์ž ์ด๋ฆ„์ด [writer]์ธ ๊ธ€๋“ค์€ ์ด์ œ ๋ชจ๋‘ ์ง€์›Œ์ฃผ์„ธ์š”!"
    @Transactional // ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ์ง€์šธ ๋• ์•ˆ์ „ํ•˜๊ฒŒ! ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด์š”.
    int deleteByWriter(String writer);
}

๐Ÿ“Œ ๊ทœ์น™์€ ์•„์ฃผ ๊ฐ„๋‹จํ•ด์š”:

๋™์ž‘ + By + ํ•„๋“œ๋ช… (๊ทธ๋ฆฌ๊ณ  ํ•„์š”ํ•˜๋ฉด And, Or๋กœ ์กฐํ•ฉ)

  • countAllByWriter: '์ „๋ถ€ ์„ธ์–ด์ค˜' (countAll) + '์ž‘์„ฑ์ž ๊ธฐ์ค€์œผ๋กœ' (ByWriter)
  • findByWriter: '์ฐพ์•„์ค˜' (find) + '์ž‘์„ฑ์ž ๊ธฐ์ค€์œผ๋กœ' (ByWriter)
  • findByTitleAndWriter: '์ฐพ์•„์ค˜' (find) + '์ œ๋ชฉ ๊ธฐ์ค€์œผ๋กœ' (ByTitle) + '๊ทธ๋ฆฌ๊ณ ' (And) + '์ž‘์„ฑ์ž ๊ธฐ์ค€์œผ๋กœ' (Writer)
  • deleteByWriter: '์ง€์›Œ์ค˜' (delete) + '์ž‘์„ฑ์ž ๊ธฐ์ค€์œผ๋กœ' (ByWriter)

์ด๋ ‡๊ฒŒ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” '์š”์ฒญ'์„ ์ด๋ฆ„์œผ๋กœ ์ž˜ ์ง€์–ด์„œ ์ ์–ด ๋†“์œผ๋ฉด, ์ปดํ“จํ„ฐ(์ •ํ™•ํžˆ๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ผ๋Š” ๋˜‘๋˜‘ํ•œ ์นœ๊ตฌ)๋Š” ์ € ์ด๋ฆ„๋งŒ ๋ณด๊ณ ๋„ ์•Œ์•„์„œ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•”ํ˜ธ(JPQL)๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„์˜ค๊ฑฐ๋‚˜, ์„ธ๊ฑฐ๋‚˜, ์ง€์›Œ์ฃผ๋Š” ๊ฑฐ์˜ˆ์š”.

@Transactional ๊ฐ™์€ ๊ฑด "์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ธ€์„ ํ•œ ๋ฒˆ์— ์ง€์šฐ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ค‘์š”ํ•œ ์ž‘์—…์€ ์ค‘๊ฐ„์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์•ˆ ๋˜๋‹ˆ๊นŒ, ์ปดํ“จํ„ฐ๊ฐ€ ํŠน๋ณ„ํžˆ ๋” ์‹ ๊ฒฝ ์จ์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ค˜!" ๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋Š” ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์‰ฌ์›Œ์š”.

 

 

๐Ÿงช ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ํ•˜๋‚˜์”ฉ ์ดํ•ดํ•ด๋ณด๊ธฐ

๐ŸŽฏ 1. ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ ๋งŒ๋“ค๊ธฐ (@BeforeEach)

for (int i = 0; i < 100; i++) {
    Board board = new Board();
    board.setBno((long)i);
    board.setTitle("title" + i);
    board.setContent("content" + i);
    board.setWriter("writer" + (i % 5)); // writer0 ~ writer4 (5๋ช…)
    ...
    boardRepository.save(board);
}

๐Ÿ‘‰ ์ด 100๊ฐœ์˜ ๊ฒŒ์‹œ๋ฌผ ์ƒ์„ฑ!
๐Ÿ‘‰ ๊ธ€์“ด์ด๋Š” writer0๋ถ€ํ„ฐ writer4๊นŒ์ง€, ๊ฐ 20๊ฐœ์”ฉ ์žˆ์–ด์š”.

 

๐Ÿ” 2. findByWriter() ํ…Œ์ŠคํŠธ

List<Board> list = boardRepository.findByWriter("writer1");
assertTrue(list.size() == 20);
 

writer1์ด ์ž‘์„ฑํ•œ ๊ธ€๋งŒ 20๊ฐœ ์ฐพ๊ธฐ!
SQL๋กœ ์“ฐ๋ฉด SELECT * FROM board WHERE writer = 'writer1'

 

 

๐Ÿ“Š 3. countAllByWriter() ํ…Œ์ŠคํŠธ

int count = boardRepository.countAllByWriter("writer1");
assertTrue(count == 20);

writer1์˜ ๊ฒŒ์‹œ๊ธ€ ๊ฐœ์ˆ˜ ์„ธ๊ธฐ!
SQL: SELECT COUNT(*) FROM board WHERE writer = 'writer1'

 

๐Ÿงจ 4. deleteByWriter() ํ…Œ์ŠคํŠธ

int deletedCount = boardRepository.deleteByWriter("writer1");
assertTrue(deletedCount == 20);

List<Board> list = boardRepository.findByWriter("writer1");
assertTrue(list.size() == 0);

writer1์ด ์“ด ๊ธ€์„ ๋ชฝ๋•… ์‚ญ์ œ!
SQL: DELETE FROM board WHERE writer = 'writer1'

 

๐Ÿง  ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ํ‚ค์›Œ๋“œ ์š”์•ฝํ‘œ

ํ‚ค์›Œ๋“œ์„ค๋ช…์˜ˆ์‹œ ๋ฉ”์„œ๋“œ
findBy ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒ findByTitle(String title)
countBy ๊ฐœ์ˆ˜ ์„ธ๊ธฐ countByWriter(String writer)
deleteBy ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ ์‚ญ์ œ deleteByWriter(String writer)
And, Or ์กฐ๊ฑด ์—ฌ๋Ÿฌ ๊ฐœ ์กฐํ•ฉ findByTitleAndWriter(String t, String w)
 

โœ… ์ •๋ฆฌํ•˜๋ฉด!

๊ฐœ๋…์„ค๋ช…
JPQL Entity ๊ธฐ๋ฐ˜ SQL ๋ฌธ๋ฒ• (์กฐ๊ธˆ ๋ณต์žกํ•˜์ง€๋งŒ ์œ ์—ฐํ•จ)
์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ SQL์„ ์ž๋™ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ
์‚ฌ์šฉ ์˜ˆ์‹œ findBy, countBy, deleteBy ๋“ฑ๋“ฑ
์žฅ์  SQL ๋ชฐ๋ผ๋„ ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ ์กฐํšŒ/์‚ญ์ œ ๊ฐ€๋Šฅ
 

๐ŸŽ ๋งˆ๋ฌด๋ฆฌ

์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋Š” Spring Data JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ตœ๊ณ ์˜ ์„ ๋ฌผ์ž…๋‹ˆ๋‹ค.
๋ณต์žกํ•œ SQL ์—†์ด๋„, ์ด๋ฆ„๋งŒ ์ž˜ ์ง€์œผ๋ฉด JPA๊ฐ€ ์•Œ์•„์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ๋‹ค ์ค˜์š”!

๋ฐ˜์‘ํ˜•