ํ”„๋กœ์ ํŠธ

[Floney] ์ž์‚ฐ ๋ฐ์ดํ„ฐ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ ์‚ฝ์งˆ๊ธฐ

sechoi 2023. 12. 14. 19:46

๐Ÿ€ https://github.com/Floney-2023

 

์šฐ๋ฆฌ ๊ฐ€๊ณ„๋ถ€์—์„œ ์ž์‚ฐ์€ ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ์ค‘ ์ง€์ถœ(-)๊ณผ ์ˆ˜์ž…(+)์˜ ํ•ฉ์„ ์˜๋ฏธํ•œ๋‹ค.

์ด ์ž์‚ฐ์ด๋ผ๋Š” ๊ฐœ๋…์€ ์„ค๊ณ„ ๊ณผ์ •๋ถ€ํ„ฐ ์–ด๋ ค์›€์ด ์žˆ์—ˆ๋Š”๋ฐ, ํ•˜๋‚˜์˜ ์ง€์ถœ ํ˜น์€ ์ˆ˜์ž…์ด ์ƒ๊ธฐ๋ฉด ๊ทธ์— ๋”ฐ๋ผ ํ•ด๋‹น ๋‚ด์—ญ ๋‚ ์งœ ์ดํ›„๋กœ ๋งค ๋‹ฌ์˜ ์ง€์ถœ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐฑ์‹ ๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

๋‹ค๋ฅธ ๊ฐ€๊ณ„๋ถ€ ์„œ๋น„์Šค๋ฅผ ์ฐธ๊ณ ํ•ด ์ž์‚ฐ์ด ๊ฐฑ์‹ ๋˜๋Š” ํŠน์ • ๊ธฐ๊ฐ„(5๋…„)์„ ์ •ํ•ด์„œ ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ๋ณ€๊ฒฝ ์‹œ ๋งค ๋ฒˆ 60๊ฐœ(5๋…„ * 12๋‹ฌ)์˜ ์ž์‚ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐฑ์‹ ๋˜๋„๋ก ์ •ํ•˜๋ฉด์„œ ์ด ๋ฌธ์ œ๋Š” ๋งˆ๋ฌด๋ฆฌ๋˜์—ˆ๋‹ค.

 

๋ฌธ์ œ ๋ฐœ์ƒ - ์˜๋„๋˜์ง€ ์•Š์€ ์ž์‚ฐ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ์ƒ์„ฑ

์•ฑ์ด ์ถœ์‹œ๋œ ํ›„, ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ์„ ์ˆ˜์ •ํ•˜๋Š” ๊ณผ์ • ์ค‘ ์ž์‚ฐ ์‚ญ์ œ ๋ฉ”์„œ๋“œ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

 

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

 

๊ธฐ์กด ์ƒํ™ฉ

 

์ž์‚ฐ ๋ฐ์ดํ„ฐ

2000๋…„ 1์›” X์ผ์˜ ํ•œ ๊ฐ€๊ณ„๋ถ€ ์ˆ˜์ž… ๋‚ด์—ญ ๊ธˆ์•ก์„ 1000์›์—์„œ 500์›์œผ๋กœ ์ˆ˜์ •ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ์ด ๋•Œ 2000๋…„ 1์›”๋ถ€ํ„ฐ 2004๋…„ 12์›”๊นŒ์ง€ ์ด 60๋‹ฌ ๋™์•ˆ ํฌํ•จ๋˜์–ด ์žˆ๋˜ ์ž์‚ฐ 1000์›์„ 500์›์œผ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

 

๊ธฐ์กด ์ฝ”๋“œ์—์„œ๋Š” '๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ์ˆ˜์ •'์ด๋ผ๋Š” ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์—์„œ ์ž์‚ฐ์„ ํฌํ•จํ•œ ๊ด€๋ จ๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ์˜ ์ˆ˜์ •์ด ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ž์‚ฐ ์ˆ˜์ •์€ '๊ธฐ์กด ๊ธˆ์•ก ์‚ญ์ œ → ๋ณ€๊ฒฝ ๊ธˆ์•ก ์ถ”๊ฐ€' ์ˆœ์„œ๋กœ ์ด๋ฃจ์–ด์กŒ๋‹ค.

 

๊ธฐ์กด ๊ธˆ์•ก ์‚ญ์ œ ํ›„
๋ณ€๊ฒฝ๋œ ๊ธˆ์•ก ์ถ”๊ฐ€ ํ›„

 

๊ตณ์ด ํ•œ ๋ฒˆ์— ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ์‚ญ์ œ์™€ ์ถ”๊ฐ€๋ผ๋Š” ๊ณผ์ •์„ ๊ฑฐ์น˜๋Š” ์ด์œ ๋Š”, ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ์ˆ˜์ • ์‹œ ํ•ด๋‹น ๋‚ด์—ญ์˜ ๋‚ ์งœ๋„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋Š” ์•„๋‹ˆ์ง€๋งŒ ๊ทธ๋Ÿด ๊ฒƒ์ด๋‹ค...) JPA์˜ ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์ด์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌ๊ฐ€ 2๋ฐฐ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š์œผ๋ฏ€๋กœ ์„ฑ๋Šฅ ๋ฉด์—์„œ๋„ ๊ดœ์ฐฎ์•˜์ง€๋งŒ Repeatable Read ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ ๊ฐฑ์‹  ์†์‹ค์ด ๋ฐœ์ƒํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด๋Š” NonUniqueResultException์ด ๋ฐœ์ƒํ•œ ์‹ค์ œ ์›์ธ์€ ์•„๋‹ˆ๋‹ค. ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ์„ ์ˆ˜์ •ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์ด๋ฏธ ํ•ด๋‹น ๋‚ด์—ญ์ด ์ €์žฅ๋˜์—ˆ์Œ์„ ์˜๋ฏธํ•˜๋ฏ€๋กœ ์ž์‚ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋  ์ผ์ด ์—†๋‹ค.

// ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ์˜ money๋ฅผ ์ž์‚ฐ์— ์ถ”๊ฐ€
public void createAsset() {

    for (int i = 0; i < FIVE_YEARS; i++) {
        Optional<Asset> asset = assetRepository.find(๋‚ ์งœ, ๊ฐ€๊ณ„๋ถ€);

        if (asset.isEmpty()) {
            Asset newAsset = new Asset();
            assetRepository.save(newAsset);
        } else {
            asset.get().update();
        }
    }
}

์‹ค์ œ ์›์ธ์€ '๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ์ถ”๊ฐ€'์— ์žˆ๋‹ค. ์ด ๋•Œ๋งŒ ์ž์‚ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง€๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— insert ๋˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ์‹œ

์œ„ ์˜ˆ์‹œ์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ์ค‘๋ณต ์ƒ์„ฑ์ด ๋ฐœ์ƒํ•œ๋‹ค.

 

์ฒซ๋ฒˆ์งธ ์ˆ˜์ • - ๋น„๊ด€์  ๋ฝ ์‚ฌ์šฉ

์šด์˜ ์„œ๋ฒ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” hotfix ์ด์Šˆ๋ผ, ์ตœ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ ๊ฒŒ ๋ฐ”๊พธ๋Š” ๋ฐฉ์‹์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ ๊ณ ์น˜๊ณ  ์‹ถ์—ˆ๋‹ค.

๋ฐ”๋กœ ์ƒ๊ฐ๋‚˜๋Š” ๊ฑด ์—ญ์‹œ๋‚˜ ํ•œ ๋ฒˆ ์จ๋ณธ ๋น„๊ด€์  ๋ฝ์ด์—ˆ๋‹ค. 

// ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ์˜ money๋ฅผ ์ž์‚ฐ์— ์ถ”๊ฐ€
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createAsset() {

    for (int i = 0; i < FIVE_YEARS; i++) {
        Optional<Asset> asset = assetRepository.findExclusively(); // ๋น„๊ด€์  ๋ฝ

        if (asset.isEmpty()) {
            Asset newAsset = new Asset();
            assetRepository.save(newAsset);
        } else {
            asset.get().update();
        }
    }
}

// ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ์˜ money๋ฅผ ์ž์‚ฐ์—์„œ ์ฐจ๊ฐ
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteAsset() {

    BookLine bookLine = bookLineRepository.findExclusively(); // ๋น„๊ด€์  ๋ฝ

    for (int i = 0; i < FIVE_YEARS; i++) {
        assetRepository.find().ifPresent(asset -> {
            asset.delete();
        });
    }
}

 

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

 

๋ˆˆ์น˜์ฑ˜๊ฒ ์ง€๋งŒ ์™„์ „ํžˆ ์ž˜๋ชป๋œ ์ฝ”๋“œ์ด๋‹ค. 

  • ํŠธ๋žœ์žญ์…˜์ด ๋ถ„๋ฆฌ๋˜์–ด ์ž์‚ฐ ๋ฉ”์„œ๋“œ์— Exception์ด ๋ฐœ์ƒํ•ด๋„ ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
  • ์ž์‚ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ •ํ•ฉ์„ฑ์ด ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ, ๋ฝ์œผ๋กœ๋Š” insert ์ค‘๋ณต์„ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค.
  • ๊ฐ€๊ณ„๋ถ€ id์™€ ๋‚ ์งœ๋กœ ์ž์‚ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š”๋ฐ ๊ฐ€๊ณ„๋ถ€ id์—๋งŒ ์ธ๋ฑ์Šค๊ฐ€ ๊ฑธ๋ ค ์žˆ์–ด ํ•ด๋‹น ๊ฐ€๊ณ„๋ถ€ id๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“  ์ž์‚ฐ ๋ฐ์ดํ„ฐ์— ๋ฝ์ด ์ „ํŒŒ๋œ๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ nGrinder๋กœ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์•˜๋”๋‹ˆ Vuser๊ฐ€ 10๋ช… ์ •๋„๋กœ ๋งค์šฐ ์ ์„ ๋•Œ๋„ DB ์ปค๋„ฅ์…˜์ด ๋ชจ์ž๋ผ๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. DB ์ปค๋„ฅ์…˜ ํ’€์€ ๊ธฐ๋ณธ ์„ค์ •์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ 10๊ฐœ์˜ ์ปค๋„ฅ์…˜๊ณผ 30์ดˆ์˜ ์ปค๋„ฅ์…˜ timeout ์‹œ๊ฐ„์ด ์žˆ์Œ์—๋„ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค. 

 

 

๋‘๋ฒˆ์งธ ์ˆ˜์ • - MySQL ์ฟผ๋ฆฌ ์ด์šฉ

์ฒซ๋ฒˆ์งธ ์ˆ˜์ •์—์„œ ์–ป์€ ๊ฒฐ๋ก ์€ ์•„๋ž˜์™€ ๊ฐ™์•˜๋‹ค.

1. ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ insert ์ค‘๋ณต์„ ํ•ด๊ฒฐํ•œ๋‹ค.
2. ๋ฝ์˜ ๋ฒ”์œ„๋ฅผ ์ž์‚ฐ ๋ฐ์ดํ„ฐ ํ•œ ๊ฐœ๋กœ ์ค„์ธ๋‹ค.

์ž์‚ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐ˜๋ณต๋ฌธ ๋‚ด์—์„œ ๋ถ„์‚ฐ ๋ฝ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋‚˜ ๊ณ ๋ฏผ์ด ๋  ๋•Œ, MySQL์˜ upsert ๋ฌธ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค. 

 

public void createAsset() {

    for (int month = 0; month < SAVED_MONTHS; month++) {
        final LocalDate currentMonth = startMonth.plusMonths(month);
        assetRepository.upsert();
    }
}

 

unique ์ œ์•ฝ์ด ๊ฑธ๋ฆฐ ์ปฌ๋Ÿผ๋“ค์„ ๊ธฐ์ค€์œผ๋กœ, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด update ์—†์œผ๋ฉด insert๋ฅผ ํ•˜๋Š” ๋ฌธ๋ฒ•์ด๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•ด ์ž์‚ฐ ์ถ”๊ฐ€ ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ์ดํ„ฐ ๋‹น ํ•œ ๊ฐœ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ค„์ด๊ณ , ๋ฝ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

upsert ์ฟผ๋ฆฌ์— ์žฅ์ ๋งŒ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์šฐ์„  MySQL์—์„œ๋งŒ ์ง€์›ํ•˜๋Š” ๋ฌธ๋ฒ•์ด๊ธฐ ๋•Œ๋ฌธ์— ํŠน์ • RDBMS์— ๋Œ€ํ•œ ์˜์กด์„ฑ์ด ์ƒ๊ธด๋‹ค. ๊ทธ๋ฆฌ๊ณ  JPA EntityListeners ๋„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•ด ๋ฆฌ์Šค๋„ˆ์—์„œ ๋ณ€๊ฒฝ๋˜๋Š” ๋ถ€๋ถ„์„ ์ง์ ‘ ์ฟผ๋ฆฌ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. 

ํ•˜์ง€๋งŒ ๋ถ„์‚ฐ ๋ฝ์„ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์•ˆ๋ณด๋‹ค๋Š” ๊ณต์ˆ˜๊ฐ€ ํ›จ์”ฌ ์ ๊ฒŒ ๋“ค์–ด ์„ ํƒํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

 

@Modifying
@Query(
        value = "insert into ASSET (date, money, book_id) values (:date, :money, :book) " +
                "on duplicate key update money = money + :money, updated_at = now()",
        nativeQuery = true
)
void upsertMoneyByDateAndBook(LocalDate date, Book book, double money);

 

์ž์‚ฐ์„ ์ œ์™ธํ•˜๋Š” ๋ฉ”์„œ๋“œ์—์„œ๋„ JPA ๋ณ€๊ฒฝ๊ฐ์ง€ ๋Œ€์‹  update ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ฝ ์‚ฌ์šฉ ์—†์ด ์ •ํ•ฉ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

์ƒˆ๋กœ์šด ๋ฌธ์ œ ๋ฐœ์ƒ - SQL ๋ฌธ๋ฒ• ์—๋Ÿฌ

๋‘๋ฒˆ์งธ ์ˆ˜์ •์„ ๋๋‚ด๊ณ  ๊ฐ„๋‹จํ•œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๊นŒ์ง€ ์ง„ํ–‰ํ•ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Œ์„ ํ™•์ธํ–ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋จธ์ง€ํ•˜๋‹ˆ ๋˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ๐Ÿ˜ฌโ˜ ๏ธ

๊ฒŒ๋‹ค๊ฐ€ ํ„ฐ์ง„ Exception์€ SQLGrammarException์œผ๋กœ ๋ฌธ๋ฒ•์ด ํ‹€๋ ธ๋‹ค๊ณ  ํ•ด์„œ ๋”์šฑ ๋‹นํ™ฉ์Šค๋Ÿฌ์› ๋‹ค. 

 

์ˆ˜์ • - ํ…Œ์ด๋ธ”๋ช… ๋Œ€/์†Œ๋ฌธ์ž ๊ตฌ๋ถ„

์–ด๋–ค ๋ฌธ์ œ์ธ์ง€ ์ƒ๊ฐํ•˜๋‹ค๊ฐ€, ๋กœ๊ทธ์—์„œ ์ฟผ๋ฆฌ๊ฐ€ ํ…Œ์ด๋ธ”๋ช…๋งŒ ๋Œ€๋ฌธ์ž๋กœ ์ฐํ˜”๋˜ ๊ฒŒ ๋– ์˜ฌ๋ž๋‹ค. (์ด๊ฒŒ ๊ธฐ์–ต์— ๋‚จ์•„์žˆ๋˜ ๊ฑธ ๋ณด๋ฉด ๋ฌด์˜์‹์ ์œผ๋กœ ๊ฑฐ์Šฌ๋ ธ๋‚˜๋ณด๋‹ค...)

@Modifying
@Query(
        value = "insert into ASSET (date, money, book_id) values (:date, :money, :book) " +
                "on duplicate key update money = money + :money, updated_at = now()",
        nativeQuery = true
)
void upsertMoneyByDateAndBook(LocalDate date, Book book, double money);

upsert ์ฟผ๋ฆฌ์—์„œ ๋‹จ์ˆœํžˆ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ํ…Œ์ด๋ธ”๋ช…๋งŒ ๋Œ€๋ฌธ์ž๋กœ ์ž‘์„ฑํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค. 

 

MySQL ๊ณต์‹ ๋ฌธ์„œ์—์„œ case sensitivity๋กœ ๊ฒ€์ƒ‰ํ–ˆ๋”๋‹ˆ ์•„๋ž˜์™€ ๊ฐ™์€ ๋‚ด์šฉ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

https://dev.mysql.com/doc/refman/8.0/en/identifier-case-sensitivity.html

How table and database names are stored on disk and used in MySQL is affected by the lower_case_table_names system variable. This variable does not affect case sensitivity of trigger identifiers. On Unix, the default value of lower_case_table_names is 0. On Windows, the default value is 1. On macOS, the default value is 2.

ํ…Œ์ด๋ธ”๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„์ด ๋””์Šคํฌ์— ์ €์žฅ๋˜๊ณ  ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹์€ lower_case_table_names ์‹œ์Šคํ…œ ๋ณ€์ˆ˜์— ์˜ํ•ด ์ขŒ์šฐ๋œ๋‹ค. ์ด ๋ณ€์ˆ˜๋Š” ํŠธ๋ฆฌ๊ฑฐ ์‹๋ณ„์ž์˜ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค. Unix์—์„œ ๊ธฐ๋ณธ ๊ฐ’์€ 0์ด๊ณ , Windows์—์„œ๋Š” 1์ด๋ฉฐ macOS์—์„œ๋Š” 2์ด๋‹ค.

๋กœ์ปฌ ์šด์˜์ฒด์ œ๋Š” macOS ์ด๊ธฐ ๋•Œ๋ฌธ์— MySQL ์„œ๋ฒ„์—์„œ ์†Œ๋ฌธ์ž๋กœ ์‚ฌ์šฉํ•ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค. 

ํ•˜์ง€๋งŒ ์šด์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„œ๋ฒ„๋Š” RDS์— ์žˆ๊ณ , RDS๋Š” Linux๋กœ unix ๊ณ„์—ด์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋””์Šคํฌ์˜ ์ €์žฅ๋œ ์†Œ๋ฌธ์ž ํ…Œ์ด๋ธ”๋ช…๊ณผ ์ฟผ๋ฆฌ์˜ ๋Œ€๋ฌธ์ž ํ…Œ์ด๋ธ”๋ช…์ด ๋‹ค๋ฅด๋‹ค๊ณ  ํŒ๋‹จํ•ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

 

MySQL ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ ์ค‘์ผ ๋•Œ๋Š” ํ•ด๋‹น ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ฝ”๋“œ์˜ ๋Œ€๋ฌธ์ž๋ฅผ ์ˆ˜์ •ํ–ˆ๋‹ค.

@Modifying
@Query(
        value = "insert into asset (date, money, book_id) values (:date, :money, :book) " +
                "on duplicate key update money = money + :money, updated_at = now()",
        nativeQuery = true
)
void upsertMoneyByDateAndBook(LocalDate date, Book book, double money);

 

๊ทผ๋ณธ์  ์›์ธ ํ•ด๊ฒฐ

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ํŒ€์›๋ถ„๊ป˜ ๊ทธ ๊ณผ์ •์„ ์„ค๋ช…ํ•˜๋˜ ์ค‘, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ์„ ๋ฐ›์•˜๋‹ค.

 

"๊ทธ๋Ÿฐ๋ฐ API ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ธธ์–ด์•ผ 1์ดˆ์ธ๋ฐ, ์–ด๋–ป๊ฒŒ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๊นŒ์š”?"

 

์งˆ๋ฌธ์„ ๋ฐ›์€ ๋‹น์‹œ์—๋Š” ์ถœ์‹œ ์งํ›„๋ผ ์ด์šฉํ•˜๋Š” ์‚ฌ๋žŒ์ด ๋งŽ์•„์„œ ์šฐ์—ฐํžˆ ๊ฒน์นœ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์‹์œผ๋กœ ๋Œ€๋‹ตํ–ˆ๋‹ค. ์—๋Ÿฌ๋Š” ๋ฐœ์ƒํ–ˆ๊ณ , ๊ทธ์— ๋Œ€ํ•œ ์›์ธ์€ ๋ช…ํ™•ํ•˜๋‹ˆ๊นŒ.

 

๊ทธ๋Ÿฌ๋‹ค ๋ฌธ๋“ ์ถœ์‹œ ์ „ ํ–ˆ๋˜ QA๊ฐ€ ๋– ์˜ฌ๋ž๋‹ค. ํ•œ ํŒ€์›์ด ์•ฑ์—์„œ '๊ฐ€๊ณ„๋ถ€ ์ƒ์„ฑํ•˜๊ธฐ' ๋ฒ„ํŠผ์„ ์—ฐํƒ€ํ•ด์„œ API ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์ „์†ก๋˜์—ˆ๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋˜‘๊ฐ™์€ ๊ฐ€๊ณ„๋ถ€๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ์ƒ์„ฑ๋˜์–ด์„œ ์•ฑ ๊ฐœ๋ฐœ์ž๋ถ„์ด ์—ฐํƒ€๋ฅผ ๋ง‰์•„์ฃผ์…จ๋‹ค.

 

๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ์ถ”๊ฐ€ API๋ฅผ ํ†ตํ•ด ์ž์‚ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ค‘๋ณต ์ƒ์„ฑ๋œ ์ด์œ ๋„ ์—ฌ๋Ÿฌ ๋ช…์ด ๋™์‹œ์— ์š”์ฒญํ•ด์„œ๊ฐ€ ์•„๋‹ˆ๋ผ 'ํ•œ ๋ช…์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์š”์ฒญํ•ด์„œ'๊ฐ€ ์•„๋‹๊นŒ ์‹ถ๋‹ค. 

 

ํ•ด๋‹น ํ˜„์ƒ์„ ์•ฑ์—์„œ ๋ง‰์€ ์ดํ›„๋ถ€ํ„ฐ, ์ˆ˜์ •ํ•œ ์ฝ”๋“œ๊ฐ€ ๋จธ์ง€๋  ๋•Œ๊นŒ์ง€ ์ค‘๋ณต ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋˜ ๊ฒƒ์„ ์ƒ๊ฐํ•˜๋ฉด ์ด์ชฝ์ด ์‹ ๋น™์„ฑ์ด ๋†’๋‹ค.

 

ํ”ํ•œ ๋™์‹œ์„ฑ ์ด์Šˆ์˜€์ง€๋งŒ ๋‹ค์–‘ํ•œ ๋‚ด์šฉ์„ ๋ฐฐ์šฐ๊ณ  ์ƒ๊ฐํ•ด๋ด์„œ ์˜๋ฏธ์žˆ๋˜ ์‚ฝ์งˆ๊ธฐ์˜€๋‹ค. โ›๏ธ