๐Ÿ”™ Backend

    RDS :: Too many connections ์˜ค๋ฅ˜ ํ•ด๊ฒฐ (mariaDB)

    RDS :: Too many connections ์˜ค๋ฅ˜ ํ•ด๊ฒฐ (mariaDB)

    ๋ฌธ์ œ์ƒํ™ฉ ์Šคํ”„๋ง ์„œ๋ฒ„๋ฅผ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰ํ–ˆ๋Š”๋ฐ ๊ฐ‘์ž๊ธฐ Too many connections ์—๋Ÿฌ ๋ฐœ์ƒ..! DB ๋Š” AWS RDS ๋กœ mariadb (t3.micro) ๋ฅผ ์‚ฌ์šฉ ์ค‘์ด๋‹ค. ์„ธํŒ… ํ™•์ธ ํ˜„์žฌ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” thread ์ˆ˜์™€ ์ง€์ •๋œ wait timeout ๊ฐ’, max connections ๊ฐ’์„ ํ™•์ธํ•ด๋ณด์ž. ์—ฐ๊ฒฐ๋œ thread ์ˆ˜ ํ™•์ธ SHOW STATUS LIKE โ€˜threads_connectedโ€™; ํ˜„์žฌ wait timeout ๊ฐ’ ํ™•์ธ SHOW VARIABLES LIKE โ€˜wait_timeoutโ€™; ํ˜„์žฌ max connections ๊ฐ’ ํ™•์ธ SHOW VARIABLES LIKE โ€˜max_connectionsโ€™; ํ™•์ธํ•ด๋ณด๋‹ˆ max connections ๊ฐ’์ด 30 ์œผ๋กœ ๋˜์–ด ์žˆ๋Š”๋ฐ, ์—ฐ๊ฒฐ๋œ th..

    RDS :: Incorrect string value: '\xEC\x9D\xB4\xEC\xA3\xBC...' for column ์˜ค๋ฅ˜ ํ•ด๊ฒฐ (mariaDB)

    RDS :: Incorrect string value: '\xEC\x9D\xB4\xEC\xA3\xBC...' for column ์˜ค๋ฅ˜ ํ•ด๊ฒฐ (mariaDB)

    ๋ฌธ์ œ์ƒํ™ฉ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ์ดํ›„ ๋ฐ›์•„์˜จ nickname ๊ฐ’์„ DB ์— ์ €์žฅํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ Incorrect string value ์—๋Ÿฌ๊ฐ€ ๋–ด๋‹ค. ๊ฒ€์ƒ‰ํ•ด๋ณด๋‹ˆ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ณด๋‚ธ ๋ฐ์ดํ„ฐ๋ฅผ DB ์— ๋„ฃ์œผ๋ ค ํ•  ๋•Œ, ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•œ๊ธ€์ด๋ฉด ์˜ค๋ฅ˜์ธ ๊ฒƒ์œผ๋กœ ๋ฌธ์ œ๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค ! DB ์˜ character set ์„ utf-8 ๋กœ ์„ค์ •ํ•˜๋ฉด ๋ฐ”๋กœ ํ•ด๊ฒฐ๋˜๋Š” ๋ฌธ์ œ. ํ•ด๊ฒฐ๋ฐฉ์•ˆ RDS ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฃน์„ ์ˆ˜์ •ํ•ด์ค€๋‹ค. ๋งŒ์•ฝ RDS์— default ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฃน์ด ์ ์šฉ๋˜์–ด ์žˆ๋‹ค๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฃน์„ ์ƒˆ๋กœ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฃน์—์„œ character ๋ฅผ ๊ฒ€์ƒ‰ํ•ด๋ณด์ž. ๊ฒ€์ƒ‰ํ•ด์„œ ๋‚˜์˜ค๋Š” character_set ๋“ค์˜ ๊ฐ’์„ ์ „๋ถ€ utf8mb4 ๋กœ ๋ฐ”๊ฟ”์ฃผ์ž. utf8 ๋กœ๋งŒ ๋ณ€๊ฒฝํ•ด๋„ ๋˜์ง€๋งŒ, ์ด๋ชจ์ง€์™€ ๊ฐ™์€ ๊ฐ’๋“ค์„ DB ์— ๋„ฃ๊ณ  ์‹ถ๋‹ค๋ฉด mb..

    Spring Boot :: @FeignClient ๋กœ ์™ธ๋ถ€ REST API ๊ฐ„ํŽธ ํ˜ธ์ถœ

    Spring Boot :: @FeignClient ๋กœ ์™ธ๋ถ€ REST API ๊ฐ„ํŽธ ํ˜ธ์ถœ

    ๋“ค์–ด๊ฐ€๋ฉด์„œ ์นด์นด์˜ค ๋ฐ ์• ํ”Œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์™ธ๋ถ€ API ์„œ๋ฒ„์— ์ ‘๊ทผํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€์•ผ ํ•˜๋Š” ์ผ์ด ๋งŽ์•„์กŒ๊ณ , API ์ฃผ์†Œ๋ฅผ ์„ค์ •ํŒŒ์ผ์— ์ €์žฅํ•œ ํ›„ ์ง์ ‘ HttpURLConnection ์„ ์ƒ์„ฑํ•ด์„œ ์—ฐ๊ฒฐํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋˜๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ ์ง์ ‘ HttpURLConnection ์„ ์—ฐ๊ฒฐํ•ด ์™ธ๋ถ€ API ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋ฉด ๋ฐ›์•„์˜จ JSON ์„ ์ง์ ‘ ํŒŒ์‹ฑํ•ด์•ผ ํ•˜๋Š” ๋ถˆํŽธํ•จ๋„ ์กด์žฌํ–ˆ๋‹ค. ์ •๋ฆฌํ•˜์ž๋ฉด, ์™ธ๋ถ€ API ๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ํ˜ธ์ถœํ•˜๊ณ  ์‹ถ์–ด์„œ ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ํŒŒ์‹ฑํ•˜๋Š” ๊ณผ์ •์ด ๋ฒˆ๊ฑฐ๋กœ์›Œ์„œ FeignClient ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ์•„๋ž˜ ์„ค๋ช…์—์„œ๋Š” ๋งŽ์ด ์˜ˆ์‹œ๋ฅผ ๋“œ๋Š” Github API ๋ฅผ ์ด์šฉํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค. FeinClient Feign ์€ Netflix ์—์„œ ๊ฐœ๋ฐœํ•œ REST Client ์ด๋‹ค. ๊ธฐ์กด์— Rest..

    EC2 :: Ubuntu Swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •

    EC2 :: Ubuntu Swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •

    EC2 ํ”„๋ฆฌํ‹ฐ์–ด ์„œ๋ฒ„๋กœ ์Šคํ”„๋ง์„ ๋„์šฐ๊ณ , CI/CD ๋ฅผ ์„ธํŒ…ํ•˜๊ณ  ํ•˜๋‹ค๋ณด๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋Š˜๋ฆฌ๋ ค๋ฉด ๊ณผ๊ธˆ์„ ํ•ด์•ผํ•˜๊ณ .. ๊ฐ„๋‹จํ•œ ์‹ค์Šต์ด๋‚˜ ๊ฐœ๋ฐœ์šฉ ํ™˜๊ฒฝ ์„ธํŒ…์ด๋ผ๋ฉด ์‚ฌ์–‘์„ ์˜ฌ๋ฆฌ๊ธฐ๋ณด๋‹ค Swap ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. Swap ํŒŒ์ผ ๋˜๋Š” ํŒŒํ‹ฐ์…˜ ํ™•์ธ sudo free -m Swap ์˜์—ญ์ด 0 ์ด๋‹ˆ ์„ค์ •์„ ํ•ด์ฃผ์ž Swap ํŒŒ์ผ ์ƒ์„ฑ sudo fallocate -l 2G /swapfile ์šฉ๋Ÿ‰์ด 2G ์ธ swapfile ์ด๋ž€ ์ด๋ฆ„์˜ ํŒŒ์ผ์„ ์ƒ์„ฑํ–ˆ๋‹ค. ์œ„ ํŒŒ์ผ์„ ์Šค์™‘ํŒŒ์ผ๋กœ ์„ค์ •ํ•˜์ž. sudo mkswap /swapfile 600์˜ permission ์„ ๊ถŒ์žฅํ•œ๋‹ค๊ณ  ํ•˜๋‹ˆ ๋ฐ”๊ฟ”์ฃผ์ž. sudo chmod 600 /swapfile Swap ํ™œ์„ฑํ™” sudo swapon /swapfil..

    JUnit :: ParameterizedTest ๋กœ ๊ฒฝ๊ณ„๊ฐ’ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

    JUnit :: ParameterizedTest ๋กœ ๊ฒฝ๊ณ„๊ฐ’ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

    ๊ฒฝ๊ณ„๊ฐ’์—์„œ ์žฅ์• ๊ฐ€ ๋งŽ์ด ์ผ์–ด๋‚œ๋‹ค ํ…Œ์ŠคํŠธํ•  ๋•Œ๋Š” input ๊ฐ’์˜ ๊ฒฝ๊ณ„๊ฐ’์— ๋Œ€ํ•ด์„œ ํ•ญ์ƒ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด์•ผ ํ•œ๋‹ค ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด ParameterizedTest ๋ฅผ ์ด์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. build.gradle ์ˆ˜์ • junit-jupiter-params ์ถ”๊ฐ€ testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ ์ฐธ์กฐ https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/ ์‹ค์Šต ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ๊ฐ’ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ ์š”๊ตฌ์‚ฌํ•ญ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ตœ์†Œ 8์ž ์ด์ƒ 12์ž ์ดํ•˜์—ฌ์•ผ ํ•œ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ 8์ž ๋ฏธ..

    JPA :: ๋ฒŒํฌ์„ฑ ์ˆ˜์ • ์ฟผ๋ฆฌ

    JPA :: ๋ฒŒํฌ์„ฑ ์ˆ˜์ • ์ฟผ๋ฆฌ

    ๋ฒŒํฌ ์—ฐ์‚ฐ ์—ฌ๋Ÿฌ ๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œ ์กฐ๊ฑด์— ๋งž๋Š” ๊ฐ์ฒด๋ฅผ ๋‹ค ๊ฐ€์ง€๊ณ  ์™€์„œ ์ˆ˜์ •ํ•  ํ•„์š” ์—†์ด DB ์ฟผ๋ฆฌ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ. JPA Bulk ์˜ˆ์ œ ์ฝ”๋“œ : ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์€ ๋‚˜์ด๋ณด๋‹ค ๋งŽ์€ ํšŒ์›๋“ค์˜ ๋‚˜์ด๋ฅผ +1 ์‹œํ‚จ๋‹ค. public int bulkAgePlus(int age) { return em.createQuery( "update Member m set m.age = m.age + 1" + " where m.age >= :age") .setParameter("age", age) .executeUpdate(); } ํ…Œ์ŠคํŠธ ์ฝ”๋“œ @Test public void bulkUpdate() { //given memberJpaRepository.save(new Member("member1", 10)); m..

    JPA :: ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ

    JPA :: ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ

    ๊ธฐ์กด JPA ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ public List findByPage(int age, int offset, int limit) { return em.createQuery("select m from Member m where m.age = :age order by m.username desc") .setParameter("age", age) .setFirstResult(offset) .setMaxResults(limit) .getResultList(); } public long totalCount(int age) { return em.createQuery("select count(m) from Member m where m.age = :age", Long.class) .setParameter("age", a..

    JPA :: Query Method ๊ธฐ๋Šฅ

    JPA :: Query Method ๊ธฐ๋Šฅ

    ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋งˆ๋ฒ•๊ฐ™์€ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค. ๋ฐ”๋กœ โ€œ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œโ€ ๊ธฐ๋Šฅ์ธ๋ฐ, ๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด 3๊ฐ€์ง€ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค. ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ฟผ๋ฆฌ ์ƒ์„ฑ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ JPA NamedQuery ํ˜ธ์ถœ @Query ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ Repository interface ์— ์ฟผ๋ฆฌ ์ง์ ‘ ์ •์˜ Method ์ด๋ฆ„์œผ๋กœ Query ์ƒ์„ฑ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์„ ๋ถ„์„ํ•ด์„œ JPQL ์ฟผ๋ฆฌ ์‹คํ–‰ ์˜ˆ) ์ด๋ฆ„๊ณผ ๋‚˜์ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํšŒ์›์„ ์กฐํšŒํ•˜๋ ค๋ฉด ? // ์ˆœ์ˆ˜ JPA Repository public List findByUsernameAndAgeGreaterThan(String username, int age) { return em.createQuery("select m from Member m where m.username = :us..