1. ์ขŒ์„ ์„ ์  ๋™์‹œ์„ฑ ์ œ์–ด โ€” Redis setIfAbsent(NX) ์„ ํƒ

๋ฐฉ์‹ ๊ฒ€ํ†  ๋‚ด์šฉ
DB ๋ฝ (pessimistic lock) ํŠธ๋žœ์žญ์…˜ ๋Œ€๊ธฐ๋กœ ์ฒ˜๋ฆฌ๋Ÿ‰ ์ €ํ•˜, ๋ฝ ํ•ด์ œ ํƒ€์ด๋ฐ ๊ด€๋ฆฌ ๋ณต์žก
Redis setIfAbsent(NX) โœ… ์›์ž์  ์—ฐ์‚ฐ์œผ๋กœ ๋‹จ ํ•˜๋‚˜์˜ ์š”์ฒญ๋งŒ ์„ฑ๊ณต ๋ณด์žฅ, TTL๋กœ ์ž๋™ ๋งŒ๋ฃŒ

๋™์ผ ์ขŒ์„์— ๋™์‹œ ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ DB ๋ฝ์€ ๋Œ€๊ธฐ ํ›„ ์ˆœ์ฐจ ์ฒ˜๋ฆฌ๋˜์–ด UX๊ฐ€ ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Redis NX ์—ฐ์‚ฐ์€ ์›์ž์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ๋งŒ ์„ฑ๊ณตํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ์ฆ‰์‹œ ์‹คํŒจ ์‘๋‹ต์„ ๋ฐ›์•„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


2. ๋ณตํ•ฉ ํ•„ํ„ฐ ๊ฒ€์ƒ‰ โ€” QueryDSL ์„ ํƒ

๋ฐฉ์‹ ๊ฒ€ํ†  ๋‚ด์šฉ
MyBatis ๋™์  SQL XML ์ž‘์„ฑ ํ•„์š”, ์ปดํŒŒ์ผ ํƒ€์ž„ ์˜ค๋ฅ˜ ๊ฐ์ง€ ๋ถˆ๊ฐ€
QueryDSL โœ… ํƒ€์ž… ์•ˆ์ „ํ•œ ๋™์  ์ฟผ๋ฆฌ, BooleanExpression null ์ฒ˜๋ฆฌ๋กœ ์กฐ๊ฑด ์ž๋™ ์ œ์™ธ

์žฅ๋ฅด ยท ์ง€์—ญ ยท ํ‚ค์›Œ๋“œ ์กฐ๊ฑด์ด ์„ ํƒ์ ์œผ๋กœ ์กฐํ•ฉ๋˜๋Š” ๋ณตํ•ฉ ํ•„ํ„ฐ ๊ฒ€์ƒ‰์—์„œ MyBatis์˜ <if> ํƒœ๊ทธ๋ณด๋‹ค QueryDSL์˜ BooleanExpression์ด null ๋ฐ˜ํ™˜ ์‹œ ์ž๋™์œผ๋กœ ์กฐ๊ฑด์—์„œ ์ œ์™ธ๋˜์–ด ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ณ  ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

private BooleanExpression genreEq(String genre) {
    return (genre != null && !genre.isEmpty()) ? QEvent.event.genre.eq(genre) : null;
}

3. MyBatis ๋Œ€์‹  JPA ์„ ํƒ

๋ฐฉ์‹ ๊ฒ€ํ†  ๋‚ด์šฉ
MyBatis SQL ์ง์ ‘ ์ œ์–ด ๊ฐ€๋Šฅ, ๋ณต์žกํ•œ ์ฟผ๋ฆฌ์— ์œ ๋ฆฌ
JPA + QueryDSL โœ… ๊ธฐ๋ณธ CRUD ์ž๋™ํ™”, ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋Š” QueryDSL๋กœ ๋ณด์™„

Route In์—์„œ MyBatis๋ฅผ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” JPA๋กœ ๊ธฐ๋ณธ CRUD๋ฅผ ์ž๋™ํ™”ํ•˜๊ณ  QueryDSL๋กœ ๋ณต์žกํ•œ ์กฐ๊ฑด ๊ฒ€์ƒ‰์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์กฐํ•ฉ์„ ์„ ํƒํ•ด ๋‘ ๊ธฐ์ˆ  ๋ชจ๋‘ ๊ฒฝํ—˜ํ–ˆ์Šต๋‹ˆ๋‹ค.