Gallery
Search
ํ๋ก์ ํธ ํ๊ธฐ
๋ฐฑ์๋ ์ฑ๋ฆฐ์ง์ ๋ํ ํ๊ณ
๋ฐฑ์๋ ์
์ฅ์์ ํ๋ก ํธ์๋์ ๋ํ ํ๊ณ
๊ธฐํ ๋๋์
[Spring][258] ํ๋ก์ ํธ ์๋ฃ ํ๊ธฐ ํ๊ณ , ๋ฐฐ์ด์ ๊ณผ ์์ฌ์ด์
Elasticsearch๋ ๋ํ ๋ด๋ถ์ ์ผ๋ก ์ ์์ ์ ์ด๋ฉฐ, ์ด๋ค ์๊ณ ๋ฆฌ์ฆ, ์๋ฃ๊ตฌ์กฐ ๋ฑ๋ฑ์ด๊ธฐ ๋๋ฌธ์ ๋น ๋ฅธ๊ฒ์ด ๋ณด์ฅ๋๋์ง์ ๋ํ ์ดํด๋ ์ถ๊ฐ ํ์
Elasticsearch๊ฐ ๋ด๋ถ์ ์ผ๋ก ์์ ์ ์ด๋ฉฐ ๋น ๋ฅธ ์ด์ ๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
1.
๋จผ์ , Elasticsearch๋ Apache Lucene์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ต๋๋ค. Lucene์ ๊ฒ์ ์์ง์ ์ฌ์ฉ๋๋ ๊ฐ๋ ฅํ ์คํ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ํ
์คํธ ์ธ๋ฑ์ฑ๊ณผ ๊ฒ์์ ํนํ๋ผ ์์ต๋๋ค.
2.
Elasticsearch๋ ๋๋์ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ๊ณผ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ทธ ์ค ํ๋๋ ์ญ์์ธํ๋ผ๋ ๊ฐ๋
์
๋๋ค. ์ญ์์ธํ๋๋ ๋จ์ด๊ฐ ์ด๋ ๋ฌธ์์ ๋ํ๋๋์ง๋ฅผ ๊ธฐ๋กํด ๋์ด ๊ฒ์ ์๋๋ฅผ ํฅ์์ํค๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
3.
Elasticsearch๋ ๋ถ์ฐ ์์คํ
์ผ๋ก ์ค๊ณ๋ผ ์์ด์ ์ฌ๋ฌ ๋
ธ๋์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ฐ ์ ์ฅํ๊ณ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. ์ด๋ ๊ฒ ํจ์ผ๋ก์จ ๋ฐ์ดํฐ ๊ฒ์ ๋ฐ ๋ถ์ ์์
์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
4.
Elasticsearch๋ ์ค์๊ฐ ๊ฒ์์ ์ง์ํ๊ณ ์์ด์ ๋ฐ์ดํฐ๊ฐ ์
๋ฐ์ดํธ๋๋๋ผ๋ ๊ฑฐ์ ์ค์๊ฐ์ผ๋ก ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ ๋๊ท๋ชจ์ ๋ฐ์ดํฐ์
์์๋ ๋์ ์ฑ๋ฅ์ ์ ๊ณตํ๋ ๋ฐ ๊ธฐ์ฌํ๊ณ ์์ต๋๋ค.
์๋ผ์คํฑ ์์น์ ๊ฒ์์ด ๋น ๋ฅธ ์ด์ ?
Elasticsearch๋ Lucene ์ธ๋ฑ์ค ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ๊ฒ์ ์์ง์
๋๋ค.
[Spring][258] Elasticsearch์ Lucene ์ธ๋ฑ์ฑ
์ง๋ฌธ์ด ๋์ฌ๋งํ ๋ถ๋ถ์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ํคํ
์ฒ ๊ธฐ์ ๋์
๋ฐฐ๊ฒฝ๋ถํฐ ์์๋๋ค.
์ ์ฒด์ ์ธ ์์ฒญ์ ํ๋ฆ?
์ ์ฒด์ ์ผ๋ก ํด๋ผ์ด์ธํธ์ HTTP ์์ฒญ์ด ์ด๋ป๊ฒ ์ฒ๋ฆฌ๋๋์ง ์๋ ค์ฃผ์ธ์.
โข
๊ธฐ๋ณธ ํ๋ฆ
๊ทธ ํ๋ฆ์์ ์นดํ์นด๋ ์ด๋ค ์ญํ ์ ํ๋์?
โข
๊ธฐ๋ณธ์ ์ผ๋ก ์ ํฌ๋ ๋ฉ์ธ์๋ฒ์, ์ด๋ฒคํธ๋ง์ ์ํ ๋
๋ฆฝ์ ์ธ ์๋ฒ๊ฐ ๋ณ๋๋ก ์กด์ฌํฉ๋๋ค.
โข
์๋ฒ๊ฐ ๋๋์ด์ก๊ณ ๊ทธ ์ค๊ฐ ์ญํ ๋ก ์นดํ์นด๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ์ญํ ์
๋๋ค.(๋ฉ์์งํ)
[Spring][258]
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ํ๋ก์ ํธ์ ๋ํ์ฌ ๋ฐํํ๋ฆ์ ๋ฐ๋ฅธ ๋ถ๋ฅ
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ํ๋ก์ ํธ์ ๋ํ์ฌ ๋ฐํํ๋ฆ์ ๋ฐ๋ฅธ ๋ถ๋ฅJPQL ์ฟผ๋ฆฌ๋ก ์ง์ ๊ฒ์ ๋ฐฉ์ ์ ํ๊ธฐ
@Repository
public interface ElasticBookSearchRepository extends ElasticsearchRepository<ElasticsearchBook, Long>, ElasticCustomBookSearchRepository {
// ์ด๊ฑด ๊ฐ๋ฐ์๊ฐ ์ง์ ์ฐ์ฐ๋ถ๋ฅผ ์์ฑํ๋ ๋ฐฉ์์
// ์ ์ด๊ถ์ด ๊ฐ๋ฐ์์ ์์
//List<ElasticsearchBook> findByBookNameContains(String keyword);
// Elasticsearch API์ ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํด์ ๋ณ๊ฒฝํด๋ณด๋ ๋ฐฉ๋ฒ
// ์ด๊ฑด ์ ์ด๊ถ์ด Spring Data Elasticsearch ์ ์์
// ์ฟผ๋ฆฌ๋ฅผ SPE๊ฐ ๋ถ์ํจ
//QueryStringQuery - ์๋ผ์คํฑ์์น ๋ด์ฅ ๊ธฐ๋ณธ ์ฟผ๋ฆฌ
@Query("{\"query_string\": {\"query\": \"?0\"}}")
List<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable);
//์กฐ๊ฑด๊ฒ์(bool)
//@Query("{\"bool\": {\"must\": {\"query_string\": {\"query\": \"?0\"}}}}")
//List<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable);
/*
must: [ํ๋] AND [์ปฌ๋ผ] = [์กฐ๊ฑด]
must_not: [ํ๋] AND [์ปฌ๋ผ] != [์กฐ๊ฑด]
should: [ํ๋] OR [์ปฌ๋ผ] = [์กฐ๊ฑด]
filter: [ํ๋] [์ปฌ๋ผ] IN ( [์กฐ๊ฑด] )
*/
}
์๋ ์ฟผ๋ฆฌ๋ฅผ @Query ์ด๋
ธํ
์ด์
์ ํตํด์ ์์ฑํ์ฌ ๊ฒ์์ ๋ฐฉ์์ ๋ณ๊ฒฝ ํ ์ ์๋ค. ๊ธฐ์กด ์ง์ ๋ด๋ถ ๋ก์ง์ ๊ตฌ์ฑํ๋ ๋ฐฉ์๊ณผ ๋ค๋ฅด๊ฒ, ๋ง์น JPQL์ ์ฌ์ฉํ๋๊ฒ ์ฒ๋ผ ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑ ํ ์ ์๋ค. ๊ทธ๋ผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฟผ๋ฆฌ๋ฌธ์ด ์ปดํ์ผ ๋จ๊ณ์์ ๊ฒ์ฌ๋์ง ์๋ ๋จ์ ์ด ์์ ๊ฒ์ผ๋ก ๋ณด์ฌ์ง๋ค.
QueryDSL๋ก ๊ตฌํํด๋ณด๊ธฐ
build.gradle ์์กด์ฑ ์ถ๊ฐ
dependencies {
implementation 'com.querydsl:querydsl-apt:4.x.x'
implementation 'com.querydsl:querydsl-elasticsearch:4.x.x'
}
QueryDSL ํ๋ฌ๊ทธ์ธ ๋ฐ ํ์คํฌ ์ค์
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
querydsl {
jpa = false
querydslSourcesDir = 'src/main/generated'
}
sourceSets {
main {
java {
srcDir 'src/main/generated'
}
}
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
ElasticBookRepository๋ฅผ ์์๋ฐ์ ElasticBookSearchRepositoryImpl.java ๊ตฌํ์ฒด์์ ๋ค์์ฒ๋ผ ๊ตฌํํ๋ค. Q๊ฐ์ฒด๋ฅผ ํตํด์ ์ฟผ๋ฆฌํฉํ ๋ฆฌ๋ฅผ ํตํด ์ฟผ๋ฆฌ๋ฅผ ๋ฉ์๋ํํ๋ก ์์ฑํ๊ฒ ๋๋ค. ์ด๋ฌ๋ฉด ์ปดํ์ผ ๋จ๊ณ์์ ๊ฐ๋ฐ์์ ์ค๋ฅ๋ฅผ ์ฐพ์ ์ ์๋ค.
[Spring][258] Elasticsearch APIs
[2:22] ELB ์ค๋ช
ํ์ค ๋ https๋ฅผ ์ธ๊ธํ์
จ๋๋ฐ, ๋ฐํ์๋ฃ์๋ ๊ทธ ๋ด์ฉ์ด ๋ฐ์๋์ด ์์ผ๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ main server์ event server์ spring architecture๋ฅผ ๋ชจ๋ ๋ณด์ฌ์ค ํ์๋ ์๊ณ , application server๋ก ์ ์ํ ๋ค์ ํด๋น server์ ์คํ์ ํ๋๋ง ๋ณด์ฌ์ฃผ๊ณ main server์ event server๊ฐ application server ๊ตฌ์กฐ๋ก ๋์ด์๋ค๊ณ ๋ณด์ฌ์ฃผ๋ฉด ๋ ๊น๋ํ ๊ฒ ๊ฐ์ต๋๋ค.
๋ฐํ์๋ฃ ์์ โ
๋๋ณธ ์์ โ
์์ ๋ฉํธ ์์ โ
[2:58] mysql DB๋ก ๋ถํฐ ๋์ ๋ฐ์ดํฐ๊ฐ ๋๊ธฐํ๋๋ค๋ ํํ๋ณด๋ค๋, logstash๊ฐ ์ ์ฒ๋ฆฌํ ๋์ ๋ฐ์ดํฐ๊ฐ elasticsearch์ ์ ์ฅ๋๊ณ , ์ด ๋ฐ์ดํฐ๊ฐ mysql์ ๋๊ธฐํ๋๊ณ ์๋ค๋ผ๊ณ ํํํ๋๊ฒ ๋ง์ ๊ฒ ๊ฐ์ต๋๋ค.
๋ฐํ์๋ฃ ์์ ์์โ
๋๋ณธ ์์ โ
์์ ๋ฉํธ ์์ โ
[Spring][258] ๋ฐํ ํผ๋๋ฐฑ ์์ [์๋ฃ]
Spring Boot
Spring Security
JWT
Swagger
Spring Data JPA
Querydsl
Elasticsearch
[Spring][258]
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ๊ธฐ์ ์คํ๊ณผ ๊ด๋ จ๋ ๊ฒ๋ค
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ๊ธฐ์ ์คํ๊ณผ ๊ด๋ จ๋ ๊ฒ๋ค
[Spring][258] ํ๋ก์ ํธ ๋ง๋ฌด๋ฆฌ ์์
์ธ์ฉ
๊ฐ์ฉ์์ต์ ํ
โข
์ง์์๋ต ์๊ฒฌ๊ฒฐ์ ๊ด๋ จ๋ด์ฉ ๋ชจ๋์ ๋ฆฌ
โข
API ๋ช
์ธ์ ํ ์ ๋ฆฌ, ์ค์จ๊ฑฐ๋ ๊ฒฐ๊ณผ์ธ ๊ฒ์ด๋ฏ๋ก ํ์๋ ๋ค๋ฅด๋ค. ์ด๊ฒ์ ๋ฏธ๋ฃจ์๋ ์ผ์ผ๋ฟ.
[Spring][258] [์ค์๊ณต์ง] ํ๋ก์ ํธ ๋ง๋ฌด๋ฆฌ๋จ๊ณ ํ์ฌ ํ์ํ ์์
์
๋ฌด๋ถ๋ด์
๋๋ค.[์๋ฃ]
Q. ๊ฒ์ ์ฑ๋ฅ์ด ์ ์ง์ ์ผ๋ก ๊ฐ์ ๋์ง ์๊ณ ํ๋ฒ์ ์ฌ๋์ต๋๋ค.
A. ์ ์ง์ ์ผ๋ก ๊ฐ์ ๋๊ฑฐ๋ ๊ทธ๋ฐ ํ
์คํธ ๊ฒฐ๊ณผ๋ฅผ ์ํ๊ฒ ์ง๋ง, ๊ทธ๋ ์ง ์์ ์๋ ์์ต๋๋ค. ์ค์ ๋ก ๊ทธ๋ ๊ธฐ๋ํฉ๋๋ค. ์คํ๋ ค ์ฑ๋ฅ์ด ๋์ ๋๋ ์์ต๋๋ค. ํ์ง๋ง ์ค์ํ๊ฑด ๊ทธ ํ
์คํธ ์๋ฃ๋ค์ ๊ทธ๋ ๋ค๊ณ ํด์ ํ์ ์๋๊ฒ์ด ์๋๋๋ค. ์คํ๋ ค ๊ทธ๋ฐ ๋ชจ์ต์ ์ฐ์ถํ๋ ค๊ณ ์กฐ์๋๋ฉด์๋๊ณ ๊ทธ๋๋ก ๋ํ๋๋๊ฒ์ด ๋ง์ต๋๋ค. ๊ทธ๋์ ์ง๊ธ ๊ฒฐ๊ณผ๊ฐ ๋ง๋๊ฒ์
๋๋ค.
Q. ํฅํ ํ๋ก์ ํธ์์ ์ด๋ค ๋ถ๋ถ์ ์ค์ ์ผ๋ก ์งํํ๋ฉด ์ข์ ๊น์?
A. FTI์ Elasticsearch๋ฅผ ๋์
ํ ๋ถ๋ถ์์ ํํ
์คํธ์ธ๋ฑ์ค์ ํธ์ฐจ๊ฐ ๋ฐ์ํ๋ ๋ถ๋ถ์ ๋ ๊น๊ฒ ํ๋ณด์์ผ๋ฉด ์ข์์๊ฒ ๊ฐ์ต๋๋ค. ๋ฌผ๋ก ํ๋ก์ ํธ ๋๋๊ณ ๋ ๊ณ์ํด์ ์ฐพ์๋ณผ ํ์๊ฐ ์์ต๋๋ค.
MySQL์ ํํ
์ธ๋ฑ์ค์ ๋ํด์ ์๊ณ ๋ฆฌ์ฆ์ด๋ ์๋ฃ๊ตฌ์กฐ๊น์ง ํ์ฅํด๋ณด๊ณ ๋ํด์ ๋ํดํธ ์์ง์ธ InnoDB์ ๋ํด์๊น์ง ๊ทธ ์ฐจ์ด๋ฅผ ๋ฐ์์ํค๋๊ฒ์ด ๋ฌด์์ธ์ง ์์๋ณด๊ธฐ๊น์ง..
๊ทธ ํธ์ฐจ์ ์์ธ, ๋ฌด์์ด ๊ทธ๋ ๊ฒ ๋ง๋ค์๋์ง ์ฐพ์๊ฐ๋ ๊ณผ์ ์ ๊ณ์ํด์ ๊ณต๋ถํด๋ณด์๋๊ฒ ๋์ ๋ ์ ์์ต๋๋ค. ๊ทธ๊ฒ ๊ทธ ๋ถ๋ถ์ ๋ํด์ ๋ค๋ฅธ ์ด๋ ๊ฐ๋ฐ์๋ณด๋ค ์ ์๋, ์ ์ด๋ฐ ํ์์ด ๋ํ๋๋์ง ์๋ ๊ฐ๋ฐ์๊ฐ ๋๋ ๊น์ ๊ณต๋ถ ๋ฐฉ๋ฒ์
๋๋ค.
ํนํ ์ด๋ฐ ๊น์ ๊ณต๋ถ๋ฅผ ํ ๋๋ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๋๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค. ๊ฐ์ฅ ๋ช
ํํ ๊ทผ๊ฑฐ์ด๊ธฐ ๋๋ฌธ์
๋๋ค.
๋ฉํ ๋์ ์ถฉ๊ณ
๊ณตํต์ ์ผ๋ก ๊ธฐ์ ์คํ๋ค์ ๋ํด์ ๊ทผ๊ฑฐ๋ฅผ ์ ์ ๋ฆฌํด์ค๊ณ ์์ง๋ง ๋ํ ๊ณ์ํด์ ๋ณด๋ค ๋ ์ ๋ฆฌํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๊ทธ ์ด์ ๋ ์ด๋ ๊ณง ๊ธฐ์ ์ ์ ํ ๋๋ ํ๋จํ๋ ๋ฅ๋ ฅ์ด๊ณ , ๊ทธ ๊ธฐ์ ์ ์ ํํ ๊ฒ์ด ๋ง๋ค๋ ๊ทผ๊ฑฐ๋ฅผ ๋ง๋๋ ๋ฅ๋ ฅ. ์ด๊ฒ ์ค๋ฌด์์ ์ค์ ๋ก ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์
๋๋ค.
ํ๋ก์ ํธ ๋์ ์์ฃผํ๋ฉด ์๋ฉ๋๋ค. ๊ฐ๋ฐ๊ณต๋ถ ์์์
๋๋ค.
๋ง๋ฌด๋ฆฌ ๋ ์ ์๋ ๋ถ๋ถ์ ๋ง๋ฌด๋ฆฌํด์ผ๊ฒ ์ง๋ง, ์์ผ๋ก ๋ํด์ผํ ๊ฒ์ด ๋ง๊ณ ,
์คํธ๋ ์ค ๊ด๋ฆฌ์ ๋ฒ์์์ ์กฐ์ ํด์ผ ํฉ๋๋ค.
[Spring][258] [5์ฃผ์ฐจ ๋ฉํ ๋ง] ์ง๋ฌธ๋ด์ฉ ์ ๋ฆฌ์ ๋ต๋ณ + ์กฐ์ธ

[Spring][258] ๊ฐ๋จ์๊ฐ ์์ ๊ตฌ์[์๋ฃ]
๋ฐํ์๋ฃ์ ๊ตฌ์ฑ
์ฒซํ๋ฉด
๋ชฉ์ฐจ
์ข
๋ฃํ๋ฉด
๋ฐํ์๋ฃ์ ์์์ ํ์ฉ
์์์ ํผํฐ ํ๋ฆ์ ์ ์ ํ ๋ด์ฉ๋ค์ ๊ฐ๊ฒฐํ๊ณ ์ถฉ๋ถํ๊ฒ ์ค๋ช
๋๋๋ก ๊ตฌ์ฑ
์ฝํฌ๋ ๊ธฐ๋ณธ
[Spring][258] ๋ฐํ ์์&PPT ๊ตฌ์ฑ[์๋ฃ]
ํ๋ก์ ํธ ๊ธฐํ ๋ฐฐ๊ฒฝ ์ค๋ช (30์ด)
โข
ํด๋น ํ๋ก์ ํธ๋ฅผ ์ด๋ ํ ๋ฐฐ๊ฒฝ๊ณผ ๋น์์ฑ์ผ๋ก ๊ธฐํํ๊ฒ ๋์๋์ง ์ค๋ช
ํด์ฃผ์ธ์.
โข
โํ์ค์์ ์ด๋ ํ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ์ฌ ์ด๋ค ๊ธฐ์ ์ ํ์ฉํ๋ฉด ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ ๊ฒ ๊ฐ์๋ค.โ
์์ฐ ์์ ์ฌ์ (1๋ถ)
โข
์๋น์ค์ ํต์ฌ์ด ๋๋ ๊ธฐ๋ฅ๋ง ์์ฐํด ์ฃผ์ธ์. ์ฆ, ํ์๊ฐ์
, ๋ก๊ทธ์ธ ๊ณผ์ ๋ฑ์ ์๋ตํด๋ ๋ฉ๋๋ค.
โข
์๋น์ค ์์ฐ์๋ ๋ณ์๊ฐ ๋ง์ต๋๋ค. ๊ฐ๋ฅํ ๋
นํ ์์์ ํ์ฉํ์ฌ ๋ฐํ๋ฅผ ์งํํด์ฃผ์ธ์.
[Spring][258] ๋ฐํ ๋๋ณธ ํ๋ฆ ๊ตฌ์[์๋ฃ]
์ด์
์ ํฌ ํ๋ก์ ํธ์ CI/CD์ ์ํํธ์จ์ด ์ํคํ
์ฒ๋ฅผ ์ค๋ช
๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ ํฌ์กฐ๋ CI/CD๋ฅผ Github Action๊ณผ AWS์ S3, Codedeploy๋ฅผ ์ด์ฉํด์ ๊ตฌํํ์์ต๋๋ค.
์ ํฌ GitHub์์ main ๋ธ๋์น๋ก pull request๋ฅผ ๋ณด๋ด๋ฉด GitHub Action์ ์ด์ฉํด์ AWS์ S3์ Codedeploy๋ฅผ ํตํด AWS EC2 ์๋ฒ๋ก ๋ฐฐํฌ๋๋ ๊ตฌ์กฐ์
๋๋ค.
๊ทธ๋ผ ์ด์ ์ ํฌ๊ฐ ์ GIthub Action์ผ๋ก CI/CD๋ฅผ ๊ตฌ์ฑํ์๋์ง ๋ง์๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ ํฌ๋ ์ด๊ธฐ Jenkins์ GitHub Action ์ฌ์ด์์ ๊ณ ๋ฏผ์ ํ์์ต๋๋ค.
Jenkins๋ ๋ณ๋์ ์๋ฒ๋ฅผ ์ค๋นํ๊ณ ์ ์ง๋ณด์ํด์ผ ํ๋ ๋น์ฉ๊ณผ ๋
ธ๋ ฅ์ด ํ์ํ๋ฉฐ, AWS CI/CD์ ํนํ๋ ๋๊ตฌ๋ค๊ณผ์ ํตํฉ์ ์ํด ์ถ๊ฐ์ ์ธ ํ๋ฌ๊ทธ์ธ์ด๋ ์ค์ ์ด ์๊ตฌ๋ฉ๋๋ค.
Jenkins๊ฐ ๊ฐ๋ ฅํ ์ปค์คํฐ๋ง์ด์ง์ ์ ๊ณตํ์ง๋ง, GitHub Actions๋ AWS์์ ํตํฉ ๋ฐ ๊ด๋ฆฌ ์ธก๋ฉด์์ ๋ ๊ฐํธํ๊ณ ํจ์จ์ ์ธ ์๋ฃจ์
์ ์ ๊ณตํฉ๋๋ค.
๋ํ GitHub Action์ ์ฌ์ฉํ๋ฉด GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์์ ๊ธด๋ฐํ ํตํฉ์ผ๋ก ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํฌํ๋ก๋ฅผ ์ฝ๊ฒ ์ค์ ํ ์ ์๊ณ ์ฝ๋ ์์ค์ CI/CD ํ์ดํ๋ผ์ธ์ด ๋์ผํ ์ฅ์์์ ๊ด๋ฆฌ๋๋ฏ๋ก ๊ฐ๋ฐ์๋ ๋ณ๋์ CI ๋๊ตฌ๋ก ์ ํํ ํ์ ์์ด ํธ๋ฆฌํ๊ฒ ์์
ํ ์ ์๊ธฐ์ ์ ํํ์์ต๋๋ค.
๊ทธ๋ผ ์ด์ ์ ํฌ ์ํํธ์จ์ด ์ํคํ
์ฒ์ ๋ํ์ฌ ์ค๋ช
ํ๊ฒ ์ต๋๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ํ ์ ์ฐ์ ELB ( ALB )์์ ํด๋ผ์ด์ธํธ๊ฐ https ํ๋กํ ์ฝ์ ์ฌ์ฉ ํ๋์ง ์ดํด๋ณด๊ณ http๋ฉด https ํต์ ์ ํ๋๋ก ์ํ์ฝ๋ 301์ ํตํด ๋ฆฌ๋๋ ์
์ ํฉ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ฉด Main Server๋ก ์ ๋ฌํด ์ค๋๋ค. ์ฌ๊ธฐ์ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ๋ฟ์ด๋ฉด ์์ฒญ์ ๋๋์ด์ ํ ์๋ฒ์์ ๋ชจ๋ ์์ฒญ์ ์ฒ๋ฆฌํ์ง ์๊ฒ ๋ถ๋ฐฐํด์ค๋๋ค. ๊ทธ ํ Main ์๋ฒ์์๋ ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ์๋น์ค๊ฐ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ ๊ตฌ์กฐ๋ฅผ ์ ์ฉํ ์ฑ
๋๋ ์๋น์ค์ธ์ง ์ฌ๋ถ๋ฅผ ํ์
ํฉ๋๋ค. ์ฑ
๋๋ ์๋น์ค๋ ์งง์ ์๊ฐ ๋๊ท๋ชจ์ ํธ๋ํฝ์ด ์์๋๋ ์๋น์ค์ด๊ธฐ์ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ ๊ตฌ์กฐ๋ฅผ ์ ์ฉํ์์ต๋๋ค. ๋ง์ฝ ์ฑ
๋๋ ์๋น์ค๊ฐ ์๋๋ผ๋ฉด ๋ฉ์ธ์๋ฒ์์ ์์ฒด์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฑ
๋๋ ์๋น์ค์ธ ๊ฒฝ์ฐ์๋ ํ๋ก๋์๋ฅผ ํตํด ์นดํ์นด๋ก ํด๋ผ์ด์ธํธ์ ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ์ด๋ฒคํธ ์๋ฒ์์๋ ์ปจ์๋จธ๋ก ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ดํ ๋ ๋์ค์ ๋ถ์ฐ๋ฝ์ ํตํด ๋์์ฑ ์ ์ด๋ฅผ ํ๋ฉฐ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ํ๋ก๋์๋ฅผ ํตํด ์นดํ์นด ์๋ฒ๋ก ๋ณด๋
๋๋ค. ๋ฉ์ธ์๋น์ค์์๋ ์ปจ์๋จธ๋ฅผ ํตํด ๋ณด๋ธ ์์ฒญ์ ํด๋นํ๋ ์ด๋ฒคํธ ์๋ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํด์ค๋๋ค.
1์ฐจ ์์ ๋ฐ ์ ๋ฆฌ
ํ๋ก์ ํธ์ CI/CD ๋ฐ ์ํํธ์จ์ด ์ํคํ
์ฒ์ ๋ํด ์๋ดํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ ํฌ ํ์ Github Action, AWS์ S3, ๊ทธ๋ฆฌ๊ณ Codedeploy๋ฅผ ํ์ฉํ์ฌ CI/CD๋ฅผ ๊ตฌ์ฑํ์ต๋๋ค.
์ด๋ฅผ ํตํด, Github์ main ๋ธ๋์น๋ก pull request๊ฐ ์ด๋ฃจ์ด์ง๋ฉด ์๋์ผ๋ก AWS EC2 ์๋ฒ๋ก ์ฝ๋๊ฐ ๋ฐฐํฌ๋๋ ์์คํ
์ ๊ฐ์ถ์์ต๋๋ค.
CI/CD ์์คํ
์ ํ์ ์์ด, ์ ํฌ๋ Jenkins์ GitHub Action ์ค ๊ณ ๋ฏผ ๋์ ํ์๋ฅผ ์ ํํ์ต๋๋ค. Jenkins๋ ๊ฐ๋ ฅํ ๋ง์ถค ์ค์ ์ด ๊ฐ๋ฅํ์ง๋ง ๋ณ๋์ ์๋ฒ ์ ์ง ๋ฐ AWS์์ ํตํฉ์ ์ํ ์ถ๊ฐ ์์
์ด ํ์ํ ๋ฐ๋ฉด, GitHub Action์ AWS์์ ํตํฉ์ด ์ฉ์ดํ๊ณ ๊ด๋ฆฌ๊ฐ ๋ ๊ฐ๋จํ์ฌ ๊ฐ๋ฐ์๊ฐ ๋ ์ฝ๊ฒ ์์
ํ ์ ์๋ ์ฅ์ ์ด ์์ต๋๋ค.
๋ค์์ผ๋ก ์ ํฌ ์ํํธ์จ์ด ์ํคํ
์ฒ๋ฅผ ์ค๋ช
ํ๊ฒ ์ต๋๋ค.
ํด๋ผ์ด์ธํธ์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ELB(ALB)๊ฐ ์ด๋ฅผ ๋ฐ์์ HTTP ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋์ง ํ์ธํ๊ณ , ํ์ํ ๊ฒฝ์ฐ HTTPS๋ก ๋ฆฌ๋๋ ์
ํฉ๋๋ค.
์์ ํ HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ์์ฒญ์ ๋ฉ์ธ ์๋ฒ๋ก ์ ๋ฌ๋๋ฉฐ, ์ด ์๋ฒ๋ ์์ฒญ์ ์ ์ ํ ๋ถ์ฐํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค.
ํนํ ๋๊ท๋ชจ ํธ๋ํฝ์ ์ฒ๋ฆฌํด์ผ ํ๋ ์ฑ
๋๋ ์๋น์ค ๊ฐ์ ๊ฒฝ์ฐ, ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ๋ฅผ ์ ์ฉํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ ์นดํ์นด๋ฅผ ํตํด ์ด๋ฒคํธ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ๋ ๋์ค์ ๋ถ์ฐ ๋ฝ์ ํ์ฉํด ๋์์ฑ์ ์ ์ดํจ์ผ๋ก์จ ํจ์จ์ ์ธ ์๋น์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, ๋ฉ์ธ ์๋น์ค๋ ์ด๋ฒคํธ ์๋ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐ์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํฉ๋๋ค.
[Spring][258] CI/CD ๋ฐ ์ํํธ ์จ์ด ์ํคํ
์ฒ ๋์ ํ๋ฆ ์ ๋ฆฌ

[Spring][258] ํ๋ก ํธ ๋์์ธ ์์
_ ์ฑ
๋๋ ์ด๋ฒคํธ ํ์ด์ง

[Spring][258] AWS EC2 Elastic Stack์ Kibana ์ธ๋ถ ์ ์ ํ๊ฒฝ ์ค์

[Spring][258] EventApply ์๋ฒ ์ฝ๋ ๋ฆฌํฉํ ๋ง ๋ฐ ๋ถํ์ ๋ธ๋์น ์ ๊ฑฐ ์์

[Spring][258] AWS EC2 Elasticsearch ์ธ๋ถ ์ ์ ํ๊ฒฝ ์ค์

[Spring][258] AWS EC2์ ๊ตฌํ๋ Elasticsearch ์ ๋ด ์ธ๋ถ(RDS) MySQL ๋ฐ์ดํฐ ์ ๋ฌ ํ
์คํธ ๋ฐ EC2 ์ค์ผ์ผ์
ํ
์คํธ ๊ธฐ์ค
MySQL Workbench 8.0์ ์ฌ์ฉํ๊ณ ๊ฒ์ ํค์๋๋ โํฉํ ์ง ์ง๊ธฐโ๋ฅผ ์ฌ์ฉํ๋ค.
๋ณ์๋ ngram_token_size = 2, innodb_ft_min_token_size = 1์ ์ค์ ํ์ฌ ์ธ๋ฑ์ฑํ๋ค.
์ธ๋ฑ์ฑ ๊ธฐ๋ฒ์ ๊ตฌ๋ถ์ ๊ธฐ๋ฒ, N gram ๊ธฐ๋ฒ์, ๊ฒ์ ๋ฐฉ์์ ์๋ ์ธ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ค.
SELECT * FROM book WHERE MATCH(book_name) AGAINST('ํฉํ ์ง ์ง๊ธฐ');
SELECT * FROM book WHERE MATCH(book_name) AGAINST('ํฉํ ์ง ์ง๊ธฐ' IN boolean mode);
SELECT * FROM book WHERE MATCH(book_name) AGAINST('+ํฉํ ์ง +์ง๊ธฐ' IN boolean mode)
์์ ๊ฒฐ๊ณผ
๊ตฌ๋ถ์ ๊ธฐ๋ฒ : keyword๋ฅผ ๊ตฌ๋ถ์๋ก ๋ถ๋ฅํ์ฌ ์ ์ฅ, ๊ฒ์ ์์๋ ์
๋ ฅ๊ฐ์ด ์ ์ฅ๋ ๊ฐ๊ณผ ์ ํํ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅ
N-Gram ๊ธฐ๋ฒ : 2๊ธ์์ฉ ๋ถ๋ฅํ์ฌ ์ ์ฅ, ๊ฒ์ ์ ์
๋ ฅ๊ฐ์ 2๊ธ์์ฉ ๋ถ๋ฅํ์ฌ ์ ์ฅํ ๊ฐ๊ณผ ์ผ์นํ๋ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅ
[Spring][258] FULLTEXT index ์ค์ ๋ฐ ๊ฒ์ ๋ช
๋ น์ด ๊ฐ ๊ฒฐ๊ณผ ํ
์คํธ

[Spring][258] AWS EC2 ubuntu ํ๊ฒฝ์ Elasticsearch ๊ตฌ์ถ

[Spring][258] ์นดํ์นด๋ฅผ ํตํ ์๋น์ค ์ํคํ
์ฒ ๊ตฌ์กฐ ๊ฐ์
๋ ๋์ค ๋ถ์ฐ ๋ฝ์ด๋?
๋ ๋์ค(Redis) ๋ถ์ฐ ๋ฝ์ ์ฌ๋ฌ ์ปดํจํฐ ๋๋ ํ๋ก์ธ์ค ๊ฐ์ ์์์ ๋์ ์ ๊ทผ์ ์ ์ดํ๊ธฐ ์ํ ๋ฉ์ปค๋์ฆ์
๋๋ค.
๋ถ์ฐ ์์คํ
์์ ์ฌ๋ฌ ๋
ธ๋๊ฐ ๋์ผํ ์์์ ์ ๊ทผํ๋ ค ํ ๋, ์ผ๊ด์ฑ๊ณผ ์์๋ฅผ ์ ์งํ๋ฉด์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ์ฐ์
๋๋ค.
๋ ๋์ค ๋ถ์ฐ ๋ฝ์ ํ์์ฑ
โข
๋์์ฑ ๊ด๋ฆฌ: ๋ณต์์ ์ธ์คํด์ค๊ฐ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์์ ํ๋ ค ํ ๋, ๋ฐ์ดํฐ ๋ถ์ผ์น๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
โข
์ผ๊ด์ฑ ์ ์ง: ๋ถ์ฐ ์์คํ
๋ด์์ ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๋ฉฐ, ์์
์ ์์๋ฅผ ๋ณด์ฅํฉ๋๋ค.
โข
๋ฐ๋๋ฝ ๋ฐฉ์ง: ๋ฐ๋๋ฝ์ ๊ฐ์งํ๊ณ ํด๊ฒฐํ๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
[Spring][258] ๋ ๋์ค ๋ถ์ฐ๋ฝ์ ์ด์ฉํ ๋์์ฑ ๋ฌธ์ ๋์
Kafka๋ฅผ ์๋ํ๋ ์ด์
1.
ํ์ฌ ์งํ ์ค์ธ ํ๋ก์ ํธ์ ์๋น์ค๋ ๋์ฉ๋ ํธ๋ํฝ์ ๋ฐ๋ ์๋น์ค์
๋๋ค.
2.
ํ์ฅ์ฑ์ด ์ค์ํ ๊ฒฝ์ฐ
3.
ํ๋ ฅ์ฑ๊ณผ ๊ณ ๊ฐ์ฉ์ฑ์ด ์ค์ํ ๊ฒฝ์ฐ
์ฆ ์ ํฌ ์๋น์ค์ ํ์ฅ์ฑ๊ณผ ์์ ์ฑ, ์๋ฒ ๊ณผ๋ถํ ๋ฐฉ์ง, ๋์์ฑ ๋ฌธ์ ๋ฅผ ์ฑ๋ฅ ์ ํํ๋ ๋ฝ์์ด ํด๊ฒฐํ๊ธฐ ์ํด ์นดํ์นด๋ฅผ ์๋ํ์์ต๋๋ค.
[Spring][258] ์นดํ์นด ๋์
์ผ๋ก ๊ตฌ์กฐ์ ์ผ๋ก ๋์์ฑ ๋ฌธ์ ์ ๋์

[Spring][258] ์ํํธ์จ์ด ์ํคํ
์ฒ ๋ฐ CI/CD ๊ฐ๋
์ฑ ์ฌ๋ฆฐ ๊ตฌ์กฐ๋ก ์์
MySQL DB์ ๋ฐ์ดํฐ์ Elasticsearch ๋ฐ์ดํฐ๋ ๋๊ธฐํ๋ก ์ผ๊ด์ฑ์ ์ ์งํด์ผ ํ๋ค.
๊ธฐ์กด MVP ๊ธฐ๋ฅ์ผ๋ก ๋์์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๋ ๋ถ๋ถ๋ค์ด ์์ต๋๋ค. ํนํ Elasticsearch์ ์ฐ๊ฒฐ๋ ๋์ ๋ชฉ๋ก์์ ์ฌ์ฉ์๋ ๋์ ๋์ถ, ๋์ ๋์ถ ์์ฝ์ ์งํ ํ ์ ์์ต๋๋ค.
์ฌ์ฉ์๊ฐ ๋์ ๋์ถ์ ์งํํ๋ฉด ๋์์ ๋์ถ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ์ ์ ์๋ Status ์ปฌ๋ผ์ POSSIBLE์์ IMPOSSIBLE๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค. ํด๋น ๊ธฐ๋ฅ์ HTML์ Ajax HTTP ์์ฒญ์ ํตํด MySQL DB์ ์๋ ๋์ book_id์ ๋์์ ์ํ๋ฅผ ์ฆ์ ๋ณ๊ฒฝํ์ง๋ง, Elasticsearch๋ MySQL์ DB๋ฅผ Logstash๋ก ๊ฐ์ ธ์์ ์ธ๋ฑ์ฑํ๊ธฐ ๋๋ฌธ์ ์ด ๊ณผ์ ์์ ์๋ก ๋ชฉ๋ก์ ๋ถ๋ฌ์์ผ ํ ํ์์ฑ์ด ์์ต๋๋ค.
Logstash์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ถ์ ์ค์ ํ์์ฑ
๊ธฐ์กด Logstash๋ฅผ ํตํด MySQL์ DB๋ฅผ ์ ๋ฌํ์ฌ ์์ธํ๋ ๋ถ๋ถ์
๋๋ค. 709๋ง๊ฐ์ ๋์ ๋ฐ์ดํฐ๋ฅผ ํ๋ฒ์ ์ฝ์ด์ค๋ ๊ณผ์ ์์ JVM์ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ํ์์ด ๋ฐ์ํ์ฌ 200๋ง๊ฐ์ฉ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํ๊ณ Elasticsearch๋ก ์ ๋ฌํ๋๋ก ๊ตฌ์ฑ๋์ด์์ต๋๋ค.
# sample.conf
input {
jdbc {
jdbc_connection_string => "jdbc:mysql://localhost:3306/team258"
jdbc_user => "inyongkim"
jdbc_password => "1234"
jdbc_driver_library => "/Users/inyongkim/.gradle/caches/modules-2/files-2.1/mysql/mysql-connector-java/8.0.25/f8b9123acd13058c941aff25f308c9ed8000bb73/mysql-connector-java-8.0.25.jar"
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
jdbc_paging_enabled => true
jdbc_page_size => 2000000 # ํ์ด์ง ํฌ๊ธฐ๋ฅผ ์กฐ์ ํ์ธ์.
statement => "SELECT * FROM book"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "book"
document_id => "%{book_id}"
}
}
ํ์ง๋ง ์ด ๋ถ๋ถ์์ ์ค์ ์ ๋ฌธ์ ์ ์ด ์์ต๋๋ค.
โข
์ต์ด ์คํ 1ํ ์ดํ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๊ณ ํด๋น ์์ค๋ ์ข
๋ฃ๋จ
[Spring][258] MySQLโLogstashโElasticsearch ์ ๋๊ธฐํ ์ฒ๋ฆฌ์์ ๋ฐ์ํ๋ ๋ฌธ์

[Spring][258] ํ์ฌ ํ๋ก์ ํธ ๋ด์์ ๋์์ฑ ๋ฌธ์
๋์์ฑ ์ ์ด๋?
๋์์ฑ ์ ์ด๋ ์ฌ๋ฌ ์ฌ์ฉ์๋ ์์คํ
์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ํ์ผ ์์คํ
๊ฐ์ ๊ณต์ ์์์ ๋์์ ์ก์ธ์คํ ๋ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ (๋์์ฑ๋ฌธ์ )๋ค์ ๊ด๋ฆฌํ๊ณ ํด๊ฒฐํ๊ธฐ ์ํ ๊ธฐ์ ์
๋๋ค.
๋ฐ์ดํฐ์ ์ผ๊ด์ฑ๊ณผ ๋ฌด๊ฒฐ์ฑ์ ์ ์งํ๋ฉด์, ์ฌ๋ฌ ์์ฒญ์ด ์๋ก ๊ฐ์ญํ์ง ์๊ณ ๋์์ ์ฒ๋ฆฌ๋ ์ ์๋๋ก ๋ณด์ฅํ๋ ์ญํ ์ ํฉ๋๋ค.
์น ์๋น์ค์์์ ๋์์ฑ ๋ฌธ์ ๋?
์น ์๋น์ค์์์ ๋์์ฑ ๋ฌธ์ ๋ ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ์น ์๋น์ค์ ๋ฆฌ์์ค์ ์ ๊ทผํ๊ฑฐ๋ ์กฐ์ํ ๋ ๋ฐ์ํ๋ ์ถฉ๋ ๋๋ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๋ฌธ์ ๋ฅผ ๋งํฉ๋๋ค.
์น ์๋น์ค๋ ๋๊ฐ ๋ง์ ์ฌ์ฉ์๊ฐ ๋์์ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ด๋ฌํ ๋ฌธ์ ๋ ํํ๊ฒ ๋ฐ์ํ ์ ์์ต๋๋ค.
[Spring][258] ์น ์๋น์ค ๋์์ฑ ์ ์ด
1. ๊ฐ์
๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ ๋ชจ๋ ์ ์ฉํด๋ณด๋ฉฐ JMeter๋ก ํ
์คํธ๋ฅผ ํ๊ณ
์ต์ข
์ ์ผ๋ก ๊ฒฐ์ ํ ๋ฐฉ๋ฒ์ ์ ํํ ๊ณผ์ ์ ์ ๋ฆฌํ์์ต๋๋ค.
2. ๋์์ฑ ํด๊ฒฐ ์ฑ๋ฅ ํ ์คํธ
์ฑ
๋๋ ์ ์ฒญ์ ๋ํ ๋์์ฑ ์ค๋ฅ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๋์ 4๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์์ต๋๋ค.
1.
๋ฎคํ
์ค
2.
๋๊ด์ ๋ฝ
[Spring][258] ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ ์ ํ๋ฆฌ์ผ์ด์
๋ ๋ฒจ์์์ ๋์์ฑ ๋ฌธ์ ๋์

[Spring][258] CI/CD GitHub Action์ ์ ํํ ์ด์

[Spring][258] ํ๋ก์ ํธ ๋ด ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ ์ ์ฉ
๋ง์ดํฌ๋ก ์๋น์ค ์ํคํ ์ฒ๋?
๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ๋ ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ์ฌ์ฉ๋๋ ์ค๊ณ ์ ๊ทผ ๋ฐฉ์ ์ค ํ๋์
๋๋ค.
์ด ์ํคํ
์ฒ๋ ํฐ ๊ท๋ชจ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์๊ณ ๋
๋ฆฝ์ ์ผ๋ก ์คํ ๊ฐ๋ฅํ ์๋น์ค๋ค์ ๋ชจ์์ผ๋ก ๋ถํ ํฉ๋๋ค.
๊ฐ ์๋น์ค๋ ํน์ ๋น์ฆ๋์ค ๊ธฐ๋ฅ์ ๋ด๋นํ๊ณ , ์๋ก ๋
๋ฆฝ์ ์ผ๋ก ๊ฐ๋ฐ, ๋ฐฐํฌ, ์ด์๋ ์ ์์ต๋๋ค.
๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ์ฃผ์ ํน์ง
1.
๋ถ์ฐ ๊ตฌ์กฐ: ์ ํ๋ฆฌ์ผ์ด์
์ ๊ธฐ๋ฅ์ด ์๊ณ ๋
๋ฆฝ์ ์ธ ์๋น์ค๋ก ๋๋์ด์ ธ ์์ผ๋ฉฐ, ๊ฐ๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ๊ฐ๋ฐ ๋ฐ ๋ฐฐํฌ๋ ์ ์์ต๋๋ค.
2.
์๋น์ค ์งํฅ: ๊ฐ ๊ธฐ๋ฅ์ ์๋น์ค๋ก์ ๊ฐ๋ณ์ ์ผ๋ก ๊ธฐ๋ฅํ๋ฉฐ, API๋ฅผ ํตํด ์๋ก ํต์ ํฉ๋๋ค.
3.
์ํ์ ํ์ฅ: ์์คํ
์ ํ์ฅํ๊ธฐ ์ํด ๊ฐ๋ณ ์๋น์ค๋ฅผ ๋ณต์ ํ๊ฑฐ๋ ๋ถ์ฐํ์ฌ ์์คํ
์ ๋ถํ๋ฅผ ๋ถ์ฐ์ํค๋ ์ํ์ ํ์ฅ์ด ๊ฐ๋ฅํฉ๋๋ค.
[Spring][258] ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ

[Spring][258] keyword ๊ฒ์ ์ keyword๋ฅผ ํฌํจํ ์ผ๋ถ ๋ฐ์ดํฐ๊ฐ ๊ฒ์๋์ง ์๋ ๋ฌธ์
MySQL์์ ์ง์ํ๋ FULLTEXT ๋ช
๋ น์ด๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ตฌ๋ถ์ ๊ธฐ๋ฒ์ ์ฌ์ฉํ๊ณ ์๊ธฐ์ ๋จ์ํ ๋ช
๋ น์ด๋ง์ผ๋ก๋ ์ธ๋ฑ์ฑ์ด ๊ฐ๋ฅํ๋ค.
ALTER TABLE book ADD FULLTEXT(book_name);
์ ๋ช
๋ น์ด๋ฅผ ์
๋ ฅํ๋ฉด ์๋์ผ๋ก ํด๋น ํ
์ด๋ธ์ ํด๋น ์ปฌ๋ผ์ ๊ธฐ์ค์ผ๋ก index๋ฅผ ์์ฑํ๋ค.
๋ค๋ง ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ๊ฒฝ์ฐ workbench์์๋ timeout์ด ์ผ์ด๋๊ธฐ์ ๋ช
๋ น ํ๋กฌํํธ์์ mysql์ ์ ๊ทผํด ๋ช
๋ น์ด๋ฅผ ์
๋ ฅํด ์ฃผ์ด์ผ ํ๋ค.
FULLTEXT index๋ฅผ ํ์ฉํ ๊ฒ์์ LIKE์ ์ ์ฌ์ฉํ์ง ์๋๋ค. MATCH ~ AGAINST๋ผ๋ FULLTEXT index๋ฅผ ์ํ ๊ฒ์ ๋ช
๋ น์ด๊ฐ ๋ฐ๋ก ์กด์ฌํ๋ค.
SELECT count(*) FROM book WHERE MATCH(book_name) AGAINST('์คํ๋ง');
์ ๋ช
๋ น์ด๋ฅผ ํตํด FULLTEXT index ๊ฒ์์ ํ์ฑํํ ์ ์๋ค.
[Spring][258] ๊ตฌ๋ถ์ ๊ธฐ๋ฒ์ ํ์ฉํ FULLTEXT indexing

[Spring][258] ๊ตฌ๋ถ์ ๊ธฐ๋ฒ์ FULLTEXT index๊ฐ 2์ ์ดํ์ ๋จ์ด๋ฅผ ๊ฒ์ํ ์ ์๋ ๋ฌธ์
FULLTEXT index
FULLTEXT ์ธ๋ฑ์ค๋ ๋ฌธ์์ด ๋ฐ์ดํฐ์ ๋ํด ์ ๋ฌธ ๊ฒ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์ธ๋ฑ์ค ์ ํ์ด๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ๋จ์ด ๋๋ ๊ตฌ๋ฌธ์ด ํฌํจ๋ ๋ฌธ์ฅ์ ๋น ๋ฅด๊ฒ ์ฐพ์ ์ ์๋ค. FULLTEXT ์ธ๋ฑ์ค๋ ์ ์ฒด ๋ฌธ์ฅ์ ํ ํฐ์ผ๋ก ๋ถํดํ์ฌ ํจ๊ณผ์ ์ผ๋ก ๊ฒ์์ ์ํํ ์ ์๋๋ก ๋ง๋ค์ด ์ค๋ค.
FULLTEXT index์๋ ๋ ๊ฐ์ง ๊ธฐ๋ฒ์ด ์๋ค.
๊ตฌ๋ถ์(Stopword) ๊ธฐ๋ฒ
โข
๊ณต๋ฐฑ, ํญ, ๋ฌธ์ฅ๊ธฐํธ, ๋๋ ์ฌ์ฉ์ ์ ์ ๋ฌธ์์ด์ ๊ตฌ๋ถ์๋ก ๋ฑ๋ก
โข
๊ตฌ๋ถ์ ๊ธฐ๋ฒ์ ์ด๋ ๊ฒ ์์ฑํ ๊ตฌ๋ถ์๋ฅผ ์ด์ฉํ์ฌ ๋ด์ฉ์ ๋ถ๋ฆฌ, ํค์๋๋ฅผ ๋ถ์ํ๊ณ ๊ฒฐ๊ณผ ๋จ์ด๋ฅผ ์ธ๋ฑ์ค๋ก ์์ฑํด ๋๊ณ ๊ฒ์์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ
โข
MySQL์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ ๋ ๊ตฌ๋ถ์๊ฐ ์์ผ๋ฉฐ, ์ด๋ฅผ ๋นํ์ฑํํ ์ ์์.
[Spring][258] FULLTEXT indexing
1. ์ ์ ํ๊ฒฝ ์ค์
build.gradle ์์กด์ฑ ์ถ๊ฐ
// Elasticsearch
implementation 'org.springframework.data:spring-data-elasticsearch:5.1.3'
application.properties ์ฐ๊ฒฐ ์ฃผ์ ์
๋ ฅ
# Elasticsearch URL ์ค์
spring.elastic.url=localhost:9200
ElasticsearchConfig.java๋ฅผ ํตํ Elasticsearch ์ค์ ์ปค๋ฅ์
application.properties์์ ์ง์ ์ฐ๊ฒฐ ํ ์๋ ์์ง๋ง ๋ชจ๋ํ์์ผ์ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณด๊ณ ์ ํ์ต๋๋ค.
@Configuration
public class ElasticsearchConfig extends ElasticsearchConfiguration {
@Value("${spring.elastic.url}")
private String elasticUrl;
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo(elasticUrl)
.build();
}
}
2. 3 Layered ์ํคํ ์ฒ์ Elasticsearch ์ฐ๊ฒฐ
[Spring][258] ํ๋ก์ ํธ์์ 3 Layered Architecture์ Elasticsearch ์ฐ๊ฒฐ ํ
์คํธ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: SSH ๊ถํ ๋ถ์กฑ GUI ๊ธฐ๋ฐ ๋ฌธ์ ํด๊ฒฐ
๋ถ์ฐ๋ฝ ์ด๋ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ ์ค ํ๋๋ก ๊ฐ์ ์์์ ์ ๊ทผํ ์ ๋ฝ์ ์ ๊ณตํ์ฌ ์ ๊ทผ์ด ์๋ฃ๋๋ฉด ๋ฝ์ ํด์ ํ์ฌ ๋ค์ ์๋ฒ์๊ฒ ๋์ด๊ฐ๋ ๋ฐฉ์์ด๋ค.
๋ถ์ฐ๋ฝ์ ์ฌ์ฉํ๋ ์ด์ ๋ ๋ถ์ฐ๋ ์๋ฒ ๋ฐ ๋ถ์ฐ๋ DBํ๊ฒฝ์์๋ ๋์์ฑ ์ด์๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํจ์ด๋ค.
๋ถ์ฐ๋ฝ์ Redis๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์๋ lettuce, redisson ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๋ฐ lettuce๋ฐฉ์์ ์คํ๋ฝ(๋ฝ์ํ๋ํ์ง ๋ชปํ ๊ฒฝ์ฐ ๊ณ์ ์์ฒญ) ๋ฐฉ์์ผ๋ก redis์ ๋ถํ๊ฐ ๊ฐ ์ ์์ด redisson์ ์ฌ์ฉํ์ฌ ๊ตฌํํด๋ณด์๋ค.
RedissonConfig๋ฅผ ์์ฑํ์ฌ Bean์ ๋ฑ๋กํ๊ณ
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
private static final String REDISSON_HOST_PREFIX = "redis://";
@Bean
public RedissonClient redissonClient() {
RedissonClient redisson = null;
Config config = new Config();
config.useSingleServer().setAddress(REDISSON_HOST_PREFIX + redisHost + ":" + redisPort);
redisson = Redisson.create(config);
return redisson;
}
}
BookApplyDonationService์ ์๋์ ๊ฐ์ด ๊ตฌํํ์๋ค.
public MessageDto createBookApplyDonationV4(BookApplyDonationRequestDto bookApplyDonationRequestDto) {
RLock lock = redissonClient.getLock(String.valueOf(bookApplyDonationRequestDto.getBookId()));
try {
if (!lock.tryLock(3, 3, TimeUnit.SECONDS)) {
log.info("๋ฝ ํ๋ ์คํจ");
throw new IllegalArgumentException("๋ฝ ํ๋ ์คํจ");
}
log.info("๋ฝ ํ๋ ์ฑ๊ณต");
bookApplyDonationService2.createBookApplyDonation(bookApplyDonationRequestDto);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
} finally {
log.info("finally๋ฌธ ์คํ");
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
log.info("์ธ๋ฝ ์คํ");
}
}
return new MessageDto("์ฑ
๋๋ ์ ์ฒญ์ด ์๋ฃ๋์์ต๋๋ค.");
}
public class BookApplyDonationService2 {
private final BookRepository bookRepository;
private final BookDonationEventRepository bookDonationEventRepository;
private final BookApplyDonationRepository bookApplyDonationRepository;
private final UserRepository userRepository;
@Transactional
public void createBookApplyDonation(BookApplyDonationRequestDto bookApplyDonationRequestDto) {
Book book = bookRepository.findById(bookApplyDonationRequestDto.getBookId())
.orElseThrow(() -> new IllegalArgumentException("๋๋ ์ ์ฒญํ ์ฑ
์ด ์กด์ฌํ์ง ์์ต๋๋ค."));
if (book.getBookApplyDonation() != null) {
throw new IllegalArgumentException("์ด๋ฏธ ๋๊ตฐ๊ฐ ๋จผ์ ์ ์ฒญํ์ต๋๋ค.");
}
BookDonationEvent bookDonationEvent = bookDonationEventRepository.findFetchJoinById(bookApplyDonationRequestDto.getDonationId())
.orElseThrow(() -> new IllegalArgumentException("ํด๋น ์ด๋ฒคํธ๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค."));
if (LocalDateTime.now().isBefore(bookDonationEvent.getCreatedAt()) ||
LocalDateTime.now().isAfter(bookDonationEvent.getClosedAt())) {
throw new IllegalArgumentException("์ฑ
๋๋ ์ด๋ฒคํธ ๊ธฐ๊ฐ์ด ์๋๋๋ค.");
}
User user = userRepository.findFetchJoinById(SecurityUtil.getPrincipal().get().getUserId()).orElseThrow(
() -> new IllegalArgumentException("ํด๋น ์ฌ์ฉ์๋ ๋์๊ด ์ฌ์ฉ์๊ฐ ์๋๋๋ค.")
);
BookApplyDonation bookApplyDonation = new BookApplyDonation(bookApplyDonationRequestDto);
bookApplyDonationRepository.save(bookApplyDonation);
bookApplyDonation.addBook(book);
user.getBookApplyDonations().add(bookApplyDonation);
bookDonationEvent.getBookApplyDonations().add(bookApplyDonation);
book.changeStatus(BookStatusEnum.SOLD_OUT);
}
}
์ ๊ตฌํ์ ๋๋ bookApplyDonation2 ๋ฅผ ์ถ๊ฐ๋ก ์์ฑํ์ฌ ๋ง๋๋ ์ฐ์ฐํจ์ด ์๋๋ฐ ์ด์ ๋ํ ๋ด์ฉ์ ๋ณ๋ ํธ๋ฌ๋ธ์ํ
๋ฌธ์์ ์ ๋ฆฌํ๊ฒ ๋ค.
[Spring][258] redisson ๋ถ์ฐ ๋ฝ ๊ตฌํ

[Spring][258] ๋์์ฑ ํด๊ฒฐ ์ฑ๋ฅ ํ
์คํธ
Redis ๋ถ์ฐ๋ฝ ๊ตฌํ ( AWS Server )
์ค์น ํ๊ฒฝ
โข
AWS EC2 Ubuntu
โข
c5.xlarge
Redis EC2์ ์ค์น
[Spring][258] AWS EC2 Redis ๋ถ์ฐ๋ฝ ๊ตฌํ

[Spring][258] Elasticsearch์ RestHighLevelClient ์ฌ์ฉ์ ์์ด ์์กด์ฑ ์ฃผ์
๋ฌธ์ , ๋ฒ์ ๊ฐ์ ํธํ ํ์ธ๊ณผ ๋ฒ์ ๋ช
์ ์ฃผ์

[Spring][258] ์๋ฒ ์ค์ผ์ผ ์
: AWS EC2 ์ธ์คํด์ค ์
๊ทธ๋ ์ด๋ ๋ฐ ์ฑ๋ฅ ํฅ์
1. ๋ฌธ์
์ฒ์์ bookApplyService.java ์์ redisson์ผ๋ก ๋ถ์ฐ๋ฝ์ ๊ตฌํํ ์ฝ๋๋ ์๋์ ๊ฐ๋ค
@Transactional
public MessageDto createBookApplyDonationV4(BookApplyDonationRequestDto bookApplyDonationRequestDto) {
RLock lock = redissonClient.getLock(String.valueOf(bookApplyDonationRequestDto.getBookId()));
try {
if (!lock.tryLock(3, 3, TimeUnit.SECONDS)) {
log.info("๋ฝ ํ๋ ์คํจ");
throw new IllegalArgumentException("๋ฝ ํ๋ ์คํจ");
}
log.info("๋ฝ ํ๋ ์ฑ๊ณต");
Book book = bookRepository.findById(bookApplyDonationRequestDto.getBookId())
.orElseThrow(() -> new IllegalArgumentException("๋๋ ์ ์ฒญํ ์ฑ
์ด ์กด์ฌํ์ง ์์ต๋๋ค."));
if (book.getBookApplyDonation() != null) {
throw new IllegalArgumentException("์ด๋ฏธ ๋๊ตฐ๊ฐ ๋จผ์ ์ ์ฒญํ์ต๋๋ค.");
}
BookDonationEvent bookDonationEvent = bookDonationEventRepository.findFetchJoinById(bookApplyDonationRequestDto.getDonationId())
.orElseThrow(() -> new IllegalArgumentException("ํด๋น ์ด๋ฒคํธ๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค."));
if (LocalDateTime.now().isBefore(bookDonationEvent.getCreatedAt()) ||
LocalDateTime.now().isAfter(bookDonationEvent.getClosedAt())) {
throw new IllegalArgumentException("์ฑ
๋๋ ์ด๋ฒคํธ ๊ธฐ๊ฐ์ด ์๋๋๋ค.");
}
User user = userRepository.findFetchJoinById(SecurityUtil.getPrincipal().get().getUserId()).orElseThrow(
() -> new IllegalArgumentException("ํด๋น ์ฌ์ฉ์๋ ๋์๊ด ์ฌ์ฉ์๊ฐ ์๋๋๋ค.")
);
BookApplyDonation bookApplyDonation = new BookApplyDonation(bookApplyDonationRequestDto);
bookApplyDonationRepository.save(bookApplyDonation);
bookApplyDonation.addBook(book);
user.getBookApplyDonations().add(bookApplyDonation);
bookDonationEvent.getBookApplyDonations().add(bookApplyDonation);
book.changeStatus(BookStatusEnum.SOLD_OUT);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
} finally {
log.info("finally๋ฌธ ์คํ");
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
log.info("์ธ๋ฝ ์คํ");
}
}
return new MessageDto("์ฑ
๋๋ ์ ์ฒญ์ด ์๋ฃ๋์์ต๋๋ค.");
}
์์ ๊ฐ์ด ์์ฑ ์ ๊ธฐ์กด 10๊ฐ์ ๋ ๋ฐ์ํ๋ ๋์์ฑ ๋ฌธ์ ๊ฐ 2~3๊ฐ๋ก ์ค์ด๋ค์์ผ๋,
2~3๊ฐ์ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์๋๋ ๋ฌธ์ ๊ฐ ์์๋ค.
2. ์์ธ
@Transactional
public MessageDto createBookApplyDonationV4(BookApplyDonationRequestDto bookApplyDonationRequestDto) {
//์๋ต
log.info("์ธ๋ฝ ์คํ");
}
}
return new MessageDto("์ฑ
๋๋ ์ ์ฒญ์ด ์๋ฃ๋์์ต๋๋ค.");
}
์์์ ์์ฑํ ์ฝ๋์ ์ผ๋ถ์ด๋ค.
@Transactional๋ก ๋ง๋ค์ด์ง ํธ๋์ญ์
์ด ๋ซํ๋ ์์ ์ ๋ฉ์๋๊ฐ ๋๋๋ ์์ ์ธ๋ฐ,
[Spring][258] redisson ๋ถ์ฐ ๋ฝ ๊ตฌํ ๋ฌธ์
Q. ํ ์ฃผ๊ฐ ์นดํ์นด์ ๋ํด์ ์๋์ ๊ฐ์ด ํ๊ตฌํ๊ณ ํ๋ก์ ํธ์ ์ ์ฉํด ๋ณด์์ต๋๋ค.
์ด๋ฌํ ๋ฐฉ๋ฒ์ด ์ฌ๋ฐ๋ฅธ ์ ๊ทผ์ธ์ง ํผ๋๋ฐฑ์ ๋ฐ๊ณ ์ถ์ต๋๋ค.
โฃ
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
์ ๊ทผ O, ๋ ์ฌ๋์๊ฒ ๋ค๋ฃฐ ํ์ O
Q. ์ํคํ
์ฒ ๊ตฌ์ฑ๊ณผ ๊ด๋ จ๋ ์ง๋ฌธ ๋ฉ์ธ์๋ฒ์ ํธ๋ํฝ์๋ฒ์ ๊ตฌ๋ถ ์ด ๋ฐฉ์์ ๋ํ ์๊ฒฌ?
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
์ํคํ
์ฒ์ ๊ตฌ์ฑ
๋ฉ์ธ,ํธ๋ํฝ ์๋ฒ๋ฅผ ๋๋๋๊ฒ๋ณด๋ค๋ ๋๋ฉ์ธ์ ๊ธฐ์ค์ผ๋ก ๋๋๋๊ฒ์ด ํ์ฅ, ์ ์ง๋ณด์์ ์ผ๋ก ์ ๋ฆฌ ํ ์ ์๋ค. โ ์ถํ ๋ฉ์ธ๊ณผ ํธ๋ํฝ์๋ฒ์ ๋ชจํธํจ์ด ๋ฐ์ํ๊ฒ ๋๋ฏ๋ก
Q. Elasticsearch์ ๋์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ธฐ ์ํด์ Logstash๋ฅผ ์ค์นํ์ฌ ์ค์ ํ์ผ์ ํตํด MySQL DB์ ๋์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํด๋ณด๊ณ ์์ต๋๋ค.
์ฐ์ ์ด๋ฌํ ๋ฐฉ๋ฒ์ด ์ฌ๋ฐ๋ฅธ ์ ๊ทผ ๋ฐฉ๋ฒ์ธ์ง ์๊ณ ์ถ์ต๋๋ค.
๋๋ ๋ฐ์ดํฐ๋ ์๋์ฒ๋ผ ๋๋์ด์ ๋ก๋ํ๋๊ฒ ๋ง๋์ง? ์๋๋ฉด JVM์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฆ๊ฐ์ํค๋์ง?
์ฒ์์ ๋ณ๋์ ์ค์ ์์ด 710๋ง๊ฑด์ ๋์๋ฅผ ํ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ๊ณ ์ ๋ฌํ๋ ์ค์ ์ ํ๊ณ JVM Heap์ OutOfMemory ๋ฌธ์ ๊ฐ ๋ํ๋ฌ์ต๋๋ค.
๋ฐ๋ผ์ jdbc_paging_enabled ์ต์
์ ์ถ๊ฐํ์ฌ 50000๊ฑด์ฉ ๋ก๋ํ๋ ๊ณผ์ ์ผ๋ก ๋๋๋ ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ๋ ๋ํ๋์ง ์์์ต๋๋ค.
์ค์ ํ์ผ์ ์ฃผ๊ธฐ์ ๋ํ ์ดํด๋ ๋ถ์กฑ)
ํ์ง๋ง 710๋ง๊ฑด์ ๋ค ๋ก๋ํ๋๋ฐ ๋ค์ 0๋ถํฐ ๋ก๋ํ๊ณ ์์ต๋๋ค.
โ ์๊ฐ ๋ฐ์ดํฐ ์นดํ
๊ณ ๋ฆฌ๋ก ํ
์คํธํด๋ณด๋ ๊ณ์ ๋ก๋ํ๋๊ฒ์ด ๋์ผํ๊ฒ ๋ํ๋ฌ๊ณ , ๋ฌด์์ธ์ง ์๊ฐํด๋ณด๋ ์ค์ ํ์ผ์ schedule => "* * * * *" # Query์ฃผ๊ธฐ ์ค์ ์ ๋ฐ๋ฅธ๊ฒ์ด๊ณ ,
๊ณ์ํด์ ๊ทธ๋์ ๋งค ๋ถ๋ง๋ค SELECT * FROM book_category ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ Elasticsearch๋ก ์ ์กํ๊ณ ์๋ ๊ฒ์ธ๊ฒ ๊ฐ์ต๋๋ค. Logstash๊ฐ ๊ณ์ํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ Elasticsearch์ ์ ์กํ๋ ๋์์ ์ํํ๋๊ฒ์ผ๋ก ์ ๋ฆฌ.
๊ทธ๋ผ ๊ณ์ ๋์๋ 50000๊ฑด์ฉ 710๋ง์ ๋ชจ๋ ๋๋ฉด ๊ณ์ํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํ ๋์์ธ๊ฒ์ด๊ณ , ์ดํด๊ฐ ๋ฉ๋๋ค.
๊ทธ๋ผ ์ฃผ๊ธฐ๊ฐ ๊ธธ๋ฉด ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง ์๊ณ ์ถ์ต๋๋ค. ๊ทธ ์ฌ์ด ์
๋ฐ์ดํธ, ์ญ์ ๋ฑ ์์ด์ง ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ์์ง ๋ก๋๋์ง ์์ ์ํ์์ ๊ฒ์์ ์ค๋ฅ๊ฐ ๋ํ๋ ์ง ๋ฑ..
[Spring][258] [4์ฃผ์ฐจ ๋ฉํ ๋ง] ์ง๋ฌธ๋ด์ฉ ์ ๋ฆฌ์ ๋ต๋ณ
[Spring][258] Jmeter์ ํตํ ๋์ฉ๋ ํธ๋ํฝ ๋ฐ์ ์ฑ๋ฅ ํ
์คํธ

[Spring][258] BookRent ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ

[Spring][258] ์น ์๋น์ค ์นดํ์นด ์ ์ฉ ์ฃผ๊ฐ ์ ๋ฆฌ

[Spring][258] Elasticsearch ๊ฒ์ ์ฑ๋ฅ ๊ฐ์ 05(X) - ๋๋ ๋ฐ์ดํฐ ๋ฃ๊ธฐ ์คํจ๊ณผ์ ๊ณผ ๋ฐ์ดํฐ ์ฌ๊ฐ๊ณต์ ํตํ ๋ฌธ์ ํด๊ฒฐ
Elasticsearch ์ค์น ์ดํ, Logstash์ Kibana ์ค์น ์ด์
Elasticsearch๋ฅผ ๋จ์ DB๋ก ๋ณด๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด๋ณด๊ณ ์ ํ์ผ๋, ๊ถ๊ทน์ ์ผ๋ก Elasticsearch๋ฅผ ์ฌ์ฉํ๋ ๋ชฉ์ ์ ํฐ ์ํฅ์ ์ฃผ๋ ๋ถ๋ถ์ด ์๋๋ผ๊ณ ํ๋จ๋์์ต๋๋ค. Logstash๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ๋ฐ์ดํฐ ์์ค์์ ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ Elasticsearch์ ์ ์กํ๋ ๊ฒ์ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ด๋ฏ๋ก ๊ณง๋ฐ๋ก ์ ์ฉํด๋ณด๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
๋ํ Kibana๋ผ๋ ๋ถ์ ํด์์ ์ด๋ค๊ฒ๋ค์ ํ์ธ ํ ์ ์๋์ง ํ์ธํ๊ณ ์ ์ด ๋ํ ์ค์นํ๋ฉด์ ELK ์คํ์ ๋ชจ๋ ์ค์นํด๋ณด๋ ๊ธฐ๋ณธ ํ๊ฒฝ ๊ตฌ์ถ์ ์งํํ๊ณ ์ ํฉ๋๋ค.
Logstash, Kibana ์ค์น
Elasticsearch ์ค์น ๊ณผ์ ์ ์๋ ๋งํฌ
brew tap elastic/tap
brew install elastic/tap/logstash-full
brew install elastic/tap/kibana-full
[Spring][258] Elasticsearch ๊ฒ์ ์ฑ๋ฅ ๊ฐ์ 04 Logstash, Kibana ์ถ๊ฐ ์ค์น ELK stack ๊ตฌ์ฑ
MySQL - Logstash - Elasticsearch์ ์ฐ๊ฒฐ์ ํ๋ ์ด์ ?
๊ฐ๋จํ mysql์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด์ย elasticsearch์์ ๊ฒ์์ ํ๊ธฐ์ํด์๋ย elasticsearchย DB์ ๋ฐ์ดํฐ๋ฅผ ๋ด์์ผํฉ๋๋ค. ์ด๋ฅผ์ํ ๋๊ตฌ๋ก์ Logstash๋ฅผ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
Logstash์ ์ฌ์ฉํ์ฌ MySQL๊ณผ Elasticsearch ๊ฐ์ ๋ฐ์ดํฐ ์ ์ก์ ์ฝ๊ฒ ์ค์ ํ ์ ์์ต๋๋ค. Logstash์ ๋ค์ํ ๋ฐ์ดํฐ ์์ค์์ ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ ์ฒ๋ฆฌํ์ฌ ๋ค๋ฅธ ์ ์ฅ์๋ก ์ ์กํ๋ ์ญํ ์ ํฉ๋๋ค.
ํด๋น ๊ณผ์ ์ Elasticsearch, Logstash, Kibana์ธ ELK ์คํ์ ์ค์นํ ์ดํ๋ก ์งํ๋๋ ๊ณผ์ ์
๋๋ค. ์๋ ๋งํฌ๋ฅผ ํตํด ์ค์น์ ์คํ ์ํ๋ฅผ ํ์ธํ ์งํํด์ผ ํฉ๋๋ค.
์ค์น์ ๊ด๋ จ๋ ๋งํฌ ์ฐธ๊ณ
์ฐ์ Logstash๋ฅผ ํตํด Elasticsearch์ ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋์ด์ผ ํ๋ค.
Logstash๋ฅผ ํตํ Elasticsearch ๋ฐ์ดํฐ ์ ๋ฌ ์ค์ ์ ์ฐจ
1.
Logstash ์ค์น: ๋จผ์ Logstash์ ์ค์นํฉ๋๋ค.
[Spring][258] Elasticsearch ๊ฒ์ ์ฑ๋ฅ ๊ฐ์ 05 MySQLโLogstash-Elasticsearch ์ฐ๊ฒฐ ์ค์ , Heap Space ๋ถ์กฑ ํธ๋ฌ๋ธ ์ํ

[Spring][258] Elasticsearch ์ค์น ์ดํ, Logstash์ Kibana ์ค์น ELK Stack ํ๊ฒฝ ๊ตฌ์ถ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: CodeDeploy ๋ฐฐํฌ ์ค ๋ฐ์ํ sh ํ์ผ ์์ฑ ์ค๋ฅ ํด๊ฒฐ ์ฌ๋ก

[Spring][258] Elasticsearch๋?
๋ฌธ์ ์ํฉ
์น ์๋น์ค ๊ตฌ์ฑ
์นดํ์นด ์๋ฒ๋ฅผ ์ฌ์ฉํ๋ ์๋น์ค์์ ๋ฉ์ธ ์๋ฒ์ ํธ๋ํฝ ์๋ฒ๊ฐ ๊ฐ๊ฐ 2๊ฐ์ฉ ์กด์ฌํฉ๋๋ค.
๋ฉ์ธ ์๋ฒ์์๋ ํ๋ก๋์๋ฅผ ํตํด ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ณ , ํธ๋ํฝ ์๋ฒ์์๋ ์ปจ์๋จธ๋ฅผ ์ฌ์ฉํด ๋ฉ์์ง๋ฅผ ๋ฐ์ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ ํ ๋ค์ ํ๋ก๋์๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋
๋๋ค.
๋ฉ์ธ ์๋ฒ์์๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ทฐ๋ฅผ ๋ฐํํฉ๋๋ค.
์๋ฒ์ ํ ํฝ์ ํํฐ์
์ ํ์ฅํ ํ์ ๋ฉ์์ง ์ฒ๋ฆฌ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
๋ฉ์ธ ์๋ฒ1์์ ํธ๋ํฝ ์๋ฒ1๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋์ ๋, ๊ฒฐ๊ณผ๋ฅผ ๋ฉ์ธ ์๋ฒ1 ์ชฝ์ผ๋ก ์ ๋ฌํ์ง ๋ชปํ๋ ๋ฌธ์ ๋ฅผ ํ์
ํ์ต๋๋ค.
๋ฌธ์ ์์ธ
[Spring][258] ํธ๋ฌ๋ธ ์ํ
: ์นดํ์นด ์ปจ์๋จธ ๊ทธ๋ฃน ์ค์ ์ค๋ฅ๋ก ์ธํ ๋ฉ์์ง ์ฒ๋ฆฌ ๋ฌธ์
Elasticsearch๋ฅผ ํ์ฉํ ์ฑ๋ฅ๊ฐ์ ์ ์ํด์ ์งํํด ๋ณผ ์์๋ฅผ ์ ๋ฆฌํด๋ณด์๋ค.
1.
Elasticsearch ์ค์น
2.
๋์ ๋ฐ์ดํฐ๋ฅผ Elasticsearch์ ๋ฃ๊ณ JMeter๋ก ํ
์คํธํด๋ณด๊ธฐ
3.
MySQL๊ณผ Elasticsearch์ ์ฑ๋ฅ ์ฐจ์ด์ ๋น๊ตํ๊ธฐ
4.
Beats ๋ฅผ ์ถ๊ฐํด๋ณด๊ณ JMeter๋ก ํ
์คํธํด๋ณด๊ธฐ
5.
์ด์ ๊ณผ ์ฐจ์ด์ ๋น๊ตํ๊ธฐ
6.
Logstash ์ถ๊ฐํด๋ณด๊ณ JMeter๋ก ํ
์คํธํด๋ณด๊ธฐ
7.
์ด์ ๊ณผ ์ฐจ์ด์ ๋น๊ตํ๊ธฐ
8.
Kibana ์ถ๊ฐํด๋ณด๊ธฐ
[Spring][258] Elasticsearch ๊ฒ์ ์ฑ๋ฅ ๊ฐ์ 02 - Elasticsearch ํ์ฉํด๋ณด๊ธฐ ์์ ์ ๋ฆฌ

[Spring][258] Kafka์์์ ํจ์จ์ ์ธ ๋ฉ์์ง ์ฒ๋ฆฌ: ์ปจ์๋จธ ๊ทธ๋ฃน ID์ ํ์ฉ

[Spring][258] Elasticsearch ์ฌ์ฉ์ ์ํ ๋ก์ปฌ ํ๊ฒฝ ๊ตฌ์ถ ๊ธฐ๋ณธ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: AWS CodeDeploy๋ฅผ ์ด์ฉํ ์น์๋น์ค ๋ฐฐํฌ ์ค ํ๊ฒฝ ๋ณ์ ๋ฌธ์

[Spring][258] Elasticsearch ๊ฒ์ ์ฑ๋ฅ ๊ฐ์ 03 - Elasticsearch ์ค์น์ ํ
์คํธ ์คํ ๋ฐ ์ค์น๊ณผ์ ์ฌ์ํ ํธ๋ฌ๋ธ์ํ

[Spring][258] Elasticsearch ๊ฒ์ ์ฑ๋ฅ ๊ฐ์ 01 - Elasticsearch๋?

[Spring][258] CI/CD AWS Ec2 ์๋ฒ ๋ค์ค ๋ฐฐํฌ

[Spring][258] BookApplyDonation์์์ N+1 ๋ฌธ์ ํด๊ฒฐ ๋ฐ ์ฑ๋ฅ ํ
์คํธ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: SSH ํค ํ์ผ ๊ถํ ๋ฌธ์ ๋ก ์ธํ ์ฐ๊ฒฐ ์คํจ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: ์ปจ์๋จธ ์๋ฒ๊ฐ ์นดํ์นด ์๋ฒ ํ ํฝ์ ํํฐ์
์ ํ ๋น ๋ฐ์ง ๋ชปํ๋ ๋ฌธ์
์ด๊ธฐ์ Kafka๋ฅผ ๋์
ํ ๋ Kafka์ ๊ตฌ์กฐ์ ํน์ฑ์ผ๋ก ๋์์ฑ ๋ฌธ์ ๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ํด๊ฒฐํ ์ ์์ ๊ฒ์ด๋ผ ๊ธฐ๋ํ์์ต๋๋ค.
Kafka๋ ๋ฉ์์ง๋ฅผ ํํฐ์
๋จ์๋ก ๊ด๋ฆฌํ๋ฉฐ, ๊ฐ ํํฐ์
์ ์์๋ฅผ ๊ฐ์ง ๋ก๊ทธ๋ก ๊ตฌ์ฑ๋์ด ์์ต๋๋ค.
๋ฐ๋ผ์ ๋จ์ผ ํํฐ์
์ ๋ํด์๋ ํ๋ก๋์์ ์ปจ์๋จธ ๊ฐ์ ๋์์ฑ ๋ฌธ์ ๊ฐ ํฌ๊ฒ ๋ฐ์ํ์ง ์์ต๋๋ค
๊ทธ๋ฌ๋ Kafka ํ๊ฒฝ์ด ํ์ฅ๋๋ฉด์ ์ฌ๋ฌ ํํฐ์
์ด ์์ฑ๋๊ณ , ๊ฐ๊ฐ์ ํํฐ์
์ ๋ํด ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ํ์ํด์ง๊ธฐ ์์ํ๋ฉด์ ๋์์ฑ ๋ฌธ์ ๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก ํด๊ฒฐ๋์ง ์๋๋ค๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
๊ฐ๊ฐ์ ํํฐ์
์ ๋ํด ๋
๋ฆฝ์ ์ผ๋ก ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ์ง๋ง, ์ ์ฒด ์์คํ
์ ๊ด์ ์์ ๋ณด๋ฉด ๋์์ ์ฌ๋ฌ ์์
์ด ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฐฉ๋ฒ์ ์ฐพ๋ ๋์ค ๋ถ์ฐ ๋ฝ์ด๋ผ๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
redis๋ ZooKeeper์ ๊ฐ์ ๋ถ์ฐ ๋ฝ ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌ์์ค์ ๋ํ ์ ๊ทผ์ ์ ์ดํจ์ผ๋ก์จ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ ๊ฒ์ผ๋ก ์ถ์ ๋ฉ๋๋ค.
์ฌ๋ฌ ์ปจ์๋จธ๊ฐ ๋์ผํ ๋ฆฌ์์ค์ ์ ๊ทผํด์ผ ํ๋ ์ํฉ์์ ZooKeeper๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ์ฐ ๋ฝ์ ๊ตฌํํจ์ผ๋ก์จ, ํ ๋ฒ์ ํ๋์ ์ปจ์๋จธ๋ง์ด ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋๋ก ์ ์ดํ๋ ๋ฐฉํฅ์ผ๋ก ๋ง์
๋๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก Kafka๋ฅผ ํตํ ๋ถ์ฐ ์ฒ๋ฆฌ์ ํจ๊ป ๋ถ์ฐ ๋ฝ์ ์ ์ ํ ์ฌ์ฉํจ์ผ๋ก์จ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฝ์์ ๋ฐ์ํ ์ ์๋ ๋ฐ๋๋ฝ์ ์ํ์ ์ค์ด๋ฉด์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉํฅ์ผ๋ก ์งํํ ๋ ค๊ตฌ ํฉ๋๋ค.
[Spring][258] Kafka๋ฅผ ํ์ฉํ ๋ถ์ฐ ์ฒ๋ฆฌ ํ๊ฒฝ์์์ ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ์ ์ํ ๊ณ ์ฐฐ
1. ๊ฐ์
ํ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ ์ฌ์ฉํด ๋ณด์์ผ๋ฉฐ
์ด ๋ฐฉ๋ฒ๋ค์ ๋ชจ๋ ํฌํจํ ํ๋ก์ ํธ๋ฅผ AWS๋ฅผ ์ด์ฉํ ์๋ฒ์ ์ฌ๋ ค์ ๊ตฌ๋ํ์๋ค.
์๋ฒ์์ ๋์์ฑ ๋ถํ ํ
์คํธ๋ฅผ ์งํํ๋ฉฐ ์ฑ๋ฅ์ ์ธก์ ํด๋ณด์๋ค.
2. ํ ์คํธ ํ๊ฒฝ
๋ฉ์ธ์๋ฒ : t2.micro(??)
ํธ๋ํฝ(์นดํ์นด) ์๋ฒ : t2.micro(??)
์นดํ์นด์๋ฒ : t3.small(??)
RDS์๋ฒ : t2.micro(??) MySQL
[Spring][258] ๋์์ฑ ํด๊ฒฐ ๊ธฐ๋ฒ๋ค Jmeter ๋ถํ ์ฑ๋ฅ ํ
์คํธ

[Spring][258] ๋ทฐํ์ด์ง ์ ์์ user์ฟผ๋ฆฌ ์ถ๊ฐ๋ก 2๋ฒ ๋ฐ์ํ๋ ๋ฌธ์ ํด๊ฒฐ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: Kafka ๋ฆฌ์ค๋(์ปจ์๋จธ)๊ฐ Kafka ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ง ๋ชปํ๋ ๋ฌธ์ ๋ฐ์ ๋ฐ ํด๊ฒฐ

[Spring][258] ๋ฌดํ์คํฌ๋กค(Infinity Scroll) ๊ธฐ๋ฅ ๊ตฌํ

[Spring][258] PagenationโSliceโLoadMoreโInfinityScroll ๋ณํ๋ก ๋ณด๋ ์ฑ๋ฅ ๋ฐ ์ฌ์ฉ์๊ฒฝํ์ ์ ํ๊ณผ์

[Spring][258] EC2 Kafka ์๋ฒ ์ ์ฉ ( ํ
์คํธ ํ๊ฒฝ : ๋ก์ปฌ )
๊ฐ์
๋ฌธํ๋น
๋ฐ์ดํฐํฌํธ์์ ๊ณต๊ณต๋์๊ด ์์ฅ๋์ ๋ชฉ๋ก์ csv ํ์ผ๋ค๋ก ๋ฐ์ ์ ๋ฆฌํ๊ณ
์์ ๋ถ๋ฅ์ฝ๋๋ฅผ BookCategory๋ก ์ ๋ฆฌํ์ฌ
DB์ ์ฝ์
ํ ๊ณผ์ ์ ์์ฑํ์๋ค.
1. Book ๋ฐ์ดํฐ ๋ค์ด๋ก๋ ๋ฐ ๊ฐ๊ณต
๋ฌธํ๋น
๋ฐ์ดํฐ ํ๋ซํผ์์ ๊ณต๊ณต๋์๊ด ์์ฅ๋์ ๋ชฉ๋ก์ ๋ค์ด๋ก๋ํ์๋ค.
22๋
3์๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ฐ์์ผ๋ฉฐ csv ํ์ผ 10๊ฐ ๊ฐ๋์ด๋ค.
ํ์๋ถ์ด ํ์ด์ฌ์ ์ด์ฉํ์ฌ ํ์ํ ๋ฐ์ดํฐ์
(์ ์, ์ถํ๋
๋, ์ฑ
์ด๋ฆ, ๋ถ๋ฅ๊ธฐํธ) ๋ง ์ถ์ถํ๊ณ ์ค๋ณต์ ์ ๊ฑฐํ์๋ค.
2. BookCategory ์์ฑ
[Spring][258] bookData ๊ฐ๊ณต ๋ฐ bookCategory MySQL ์ฝ์

[Spring][258] ๋๋ณด๊ธฐ(No Offset) ๊ธฐ๋ฅ ๊ตฌํ

[Spring][258] Page์ Slice๋ฅผ ํตํ ๋ฐ์ดํฐ ํธ์ถ ์ ์ด

[Spring][258] Kafka ์ ์ฉํ ์๋ฒ ๊ตฌ์กฐ ๋ณ๊ฒฝ

[Spring][258] ํ๋ก์ ํธ์ Kafka ์๋น์ค ์ ์ฉ

[Spring][258] Kafka Consumer Group ์ค์ ์ ๋ํ ๊ณ ์ฐฐ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: Jackson ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ LocalDateTime ํ์
์ฒ๋ฆฌ ๋ฌธ์ ํด๊ฒฐ

[Spring][258] Kafka๋ฅผ ํ์ฉํ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ: ์์คํ
๊ตฌ์กฐ ๋ฐ ๊ฐ์ ๋ฐฉํฅ ๊ณ ์ฐฐ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: Spring Kafka๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์์ง๋ฅผ ์ก์์ ํ๋ ๊ณผ์ ์์ org.springframework.kafka.KafkaException: Seek to current after exception ์ค๋ฅ๊ฐ ๋ฐ์

[Spring][258] ์นดํ์นด ์ ์ฉ์ ์ ์ ์ : ํจ์จ์ ์ธ ์์คํ
๊ตฌ์กฐ ์ค๊ณ๋ฅผ ์ํ ๊ณ ์ฐฐ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: Kafka Consumer ์๋น์ค๊ฐ `bookDonationEventApplyOutput` ํ ํฝ์ผ๋ก๋ถํฐ ๋ฉ์์ง๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ ๋์ค `List<UserResponseDto>` ํ์
์ผ๋ก์ ๋ณํ ๊ณผ์ ์์ `ListenerExecutionFailedException` ์ค๋ฅ๊ฐ ๋ฐ์
Q. ๋ฌธ์ ์ด๋ฒ์ฃผ๋ถํฐ ์ฑ๋ฅ ๊ฐ์ ์ ๋ํ ์งํ ๋ฐฉ๋ฒ๊ณผ ์ง์งํ๊ฒ ๊ณ ๋ฏผํด๋ณด๊ณ ์์ต๋๋ค. ๋ฌธ์ ํด๊ฒฐํ๋ ๊ณผ์ ์ ์๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์คํ ๋ฆฌ๋ฅผ ์ ๋ฆฌํด๋๊ฐ๋ฉด ๋ ๊น์?
๋ฌธ์ ์ํฉ โ ํด๊ฒฐ๋ฐฉ์ โ ์๊ฒฌ์กฐ์จ โ ์๊ฒฌ๊ฒฐ์ โ ๋น๊ต ๊ฒฐ๊ณผ๋ค ์์ง๊ณผ ์ ๋ฆฌ..
๋ฌธ์ ์ํฉ)
๊ธฐํ ๋น์ ๊ฒ์ ๊ธฐ๋ฅ๊ณผ ํ์ด์ง ๊ธฐ๋ฅ ๊ฐ์ ์ด ๊ฐ์ฅ ์ค์ํ ํ ํฝ์ด ๋ ๊ฒ์ผ๋ก ์์ธกํ์์ต๋๋ค.
ํ์ฌ MVP ๊ตฌํ ๋จ๊ณ์์๋ ์ค์ ์ด์๋๊ณ ์๋ ์๋น์ค๋ค๊ณผ ๋น๊ตํ์๋ ์ฌ์ฉ์๊ฐ ๋ถํธํจ์ ๋๋ ์ ์์ ์์ค์ผ๋ก ์ฑ๋ฅ์ ์ด์์ด ์์์ ๋๋ผ๊ณ ์์ต๋๋ค.
(์ข ๋ ๊ตฌ์ฒด์ ์ด์ฌ์ผ ํ๋์ง?)
ํด๊ฒฐ๋ฐฉ์)
๊ธฐ๋ณธ์ ์ผ๋ก ์ ํฌ๊ฐ ํ ์ ์๋ ๋ฐฉ์ ํ์์ ๊ตฌ๊ธ๋ง, ํ ํ๋ก์ ํธ ๋ ํผ๋ฐ์ค ์ฒดํฌ ์ ๋๊ฐ ๋ง๋์ง.
์ ํฌ๊ฐ ํ๊ณ ์๋ ์์ค์ ๊ตฌ๊ธ๋ง, ChatGPT๋ฅผ ํตํ ํค์๋๋ฅผ ์ต๋ํ ํ์ํ๊ณ ์ ์ฉํ ์ ์๋ ๊ธฐ์ ์คํ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ด๋ค๊ฒ์ด ์๋์ง ์ฐพ๊ณ ์์ต๋๋ค.
์๊ฒฌ์กฐ์จ)
ํ์ ์ผ๋ก ํ์๋ฅผ ํตํด์ ์ ๋ด์ฉ์ ๋ํด์ ๊ณต์ ํ๋ฉด์ ์ ์ ํ ๊ธฐ์ ์คํ์ธ์ง๋ฅผ ํ๋จํด๋ด
๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฒฝํ์๊ฐ ์๊ธฐ๋๋ฌธ์ ์ฌ์ค ์ด ๋ถ๋ถ์ ์ ๋ขฐ๋๊ฐ ๋ถ์กฑํฉ๋๋ค. ๋ ๋ง์ ์๋ฃ๋ฅผ ํ์ํด๋ณด๋ ๊ฒ์ด ํด๋ต์ธ์ง ๊ถ๊ธํฉ๋๋ค.
์๊ฒฌ๊ฒฐ์ )
์ด๋ ํ ๊ฐ์ ์ฌํญ ํ ํฝ ํ๋์ ๋ํด ๋ช๊ฐ์ง ๊ธฐ์ ์คํ์ ์ฌ์ฉํด๋ณด์๋ ์๊ฒฌ์ด ๋ชจ์๋ค๋ฉด,
์๋ก ๊ฒ์๊ธฐ๋ฅ ๊ฐ์ ์์๋ ์๋์ ๊ฐ์ ์๊ฒฌ๋ค์ ๋ชจ์ ์ ์์์ต๋๋ค.
โฃ
์
๋ฌด ๋ถ๋ด์ ์ด๋ค์์ผ๋ก ํ๋ ๊ฒ์ด ํจ์จ์ ์ด๊ณ ํ์ ์ฒด๊ฐ ์ฑ๋ฅ๊ฐ์ ์ ์ฐธ์ฌํ ๋๋์ ์ค์ง ๊ถ๊ธํฉ๋๋ค.
ํ์์ด ๋ชจ๋ ๊ฐ์ ์ ์ฉํด๋ณด๊ณ ๋น๊ตํด๋ณด๋ ๊ฒ์ ๋ญ๊ฐ ํ์ ์ธ ์์ง์์ด ์๋๊ฒ ๊ฐ์ ๋๋์
๋๋ค. ๋ชจ๋๊ฐ ๋์ผํ๊ฒ์ ํด๋ณด๋ ์ค๋ ฅ์ ์ธ ํฅ์์ ๊ฐ์ฅ ์ข์ ๊ฒ ๊ฐ์ง๋ง ๋ง์น ๊ทธ๋ฅ ๋์ผํ ํ๋ก์ ํธ๋ฅผ ๊ฐ์ธํ๋ก์ ํธ์ฒ๋ผ ์งํํ๋ ๋๋์ผ ๊ฒ ๊ฐ์ต๋๋ค..
์๋๋ฉด ์ผ๊ด์ฑ์ ์ ์งํ๊ธฐ ์ํด์ ์ ์ฒด ํ ํฝ์ ๋งก์์ ์งํํด๋ณด๋๊ฒ ๋ง๋์ง
์๋๋ฉด ์ ๊ฐ ๊ตฌ๋ถํด ๋ ์ค๊ฐ ํ ํฝ ์ ๋๋ก ๋ถ๋ดํ๋๊ฒ์ด ์ข์์ง
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
ํ์ ์ผ๋ก ํ์๋ฅผ ํตํด์ ์ ๋ด์ฉ์ ๋ํด์ ๊ณต์ ํ๋ฉด์ ์ ์ ํ ๊ธฐ์ ์คํ์ธ์ง๋ฅผ ํ๋จํด๋ด
๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฒฝํ์๊ฐ ์๊ธฐ๋๋ฌธ์ ์ฌ์ค ์ด ๋ถ๋ถ์ ์ ๋ขฐ๋๊ฐ ๋ถ์กฑํฉ๋๋ค. ๋ ๋ง์ ์๋ฃ๋ฅผ ํ์ํด๋ณด๋ ๊ฒ์ด ํด๋ต์ธ์ง ๊ถ๊ธํฉ๋๋ค.
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
์
๋ฌด ๋ถ๋ด์ ์ด๋ค์์ผ๋ก ํ๋ ๊ฒ์ด ํจ์จ์ ์ด๊ณ ํ์ ์ฒด๊ฐ ์ฑ๋ฅ๊ฐ์ ์ ์ฐธ์ฌํ ๋๋์ ์ค์ง ๊ถ๊ธํฉ๋๋ค.
ํ์ฌ๋ ๋ถ๋ดํ๋๋ผ๋ ๋ชจ๋ ๊ฒ์ ํผ์ ํ ์๋ ์์ผ๋ฉฐ, ๋ชจ๋ ๊ฒ์ ๊ฐ์ด ํ๋๊ฒ๋ ๋ฌธ์ ๊ฐ ์๋ค.
๊ฒฐ๊ตญ ํํ๋ก์ ํธ๊ธฐ ๋๋ฌธ์ ๋ถ๋ดํด์ผ ํ๋ฉฐ, ํ๋ก์ ํธ๊ฐ ์ข
๋ฃ๋๋๋ผ๋ ๋ค๋ฅธ ํ์์ ์ฝ๋, ๊ธฐ๋ฅ์ ๋ด๊ฐ ์ง์ ๋ค๋ค๋ณด๊ณ ์ฐจ์ด์ ์ ๋๋ผ๊ณ , ๊ทธ ์ ํ๊ณผ์ ์ ์ดํด๋ณผ ํ์๊ฐ ์๋ค.
์ด๋ฌํ ํ๋ก์ ํธ ๋ณต๊ธฐ๋ฅผ ํด๋ณด๋ฉด์ ์ฅ๊ธฐ์ ์ผ๋ก ๋๋ง์ ์ ๋ฆฌ๋ฅผ ํด๋ณด๋ฉด์ ๊ณต๋ถํด์ผ ํ๋ค.
Q. ๋ฌธ์ ํด๊ฒฐ์ ์ํ ๋ง์ ๊ธฐ์ ์ ํด๊ฒฐ ๋ฐฉ์๋ค์ด ์์๋๋ฐ, ๋ธ๋ก๊ทธ๋ ๊ณต์ ๋ฌธ์๋ ๊ฐ ๊ธฐ์ ๋ค์ ์ฅ ๋จ์ ๋ค์ ์ค๋ช
ํด๋ ์๋ฟ์ง ์๋ ๋ถ๋ถ๋ค์ด ๋ง์ ์ฌ๋ฌ ๊ธฐ์ ๋ค์ ํ๋ก์ ํธ์ ์ ์ฉํด ๋ณด๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค.
ํ๋ก์ ํธ๋ฅผ ํ ๋, ๊ฐ ๊ธฐ์ ๊ฐ์ ์ฅ ๋จ์ ์ ๊ธฐ์ค์ผ๋ก ๊ฐ์ฅ ํ์ํ ๊ฒ์ ์ ํํ ํ ํ๋๋ง ์ ์ฉํด ํ๋ก์ ํธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์ข์์ง, ์๋๋ฉด ๊ฐ ๊ธฐ์ ๋ณ ๋ฉ์๋๋ฅผ ์์ฑํด์ ์ผ๋จ ์ฝ๋๋ฅผ ์์ฑํด ๋ณด๋ ๊ฒ์ด ์ข์์ง ๊ถ๊ธํฉ๋๋ค.
๋ํ ํ๋ก์ ํธ์์ ํด๋น ๊ธฐ์ ๋ค์ ๋ํ ๋ถ์์ ํ๋ก์ ํธ ๋ด์์ ๋๋ธ ํ์, ๋ถ์ ๊ณผ์ ๋ค์ ์ฝ๋๋ก ๋จ๊ฒจ๋๋ ๊ฒ์ด ์ข์์ง, ๊ทธ ์ค ๊ฐ์ฅ ์ข๋ค๊ณ ํ๋จํ ๋ฒ์ ์ ์ ์ธํ๋ฉด ๋ชจ๋ ์ญ์ ํ๋ ๊ฒ์ด ์ณ์ ๊ฒ์ธ์ง ๊ถ๊ธํฉ๋๋ค.
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
๊ธฐ์ ์คํ์ ์จ๋ด์ผ ์๋๊ฒ์ด๊ธฐ ๋๋ฌธ์, ํ์ต๊ณผ์ ์์ ํ์ฌ ์ฌ์ฉํ๋๊ฒ์ ์ฐ์ ํ๋๊ฒ์ ์ด์ฉ์์๋ ์ ํ์ด๊ณ , ํ๋ฆฐ ์ ํ์ด ์๋๋ค. ์ง์ ํด๋ณด๋ ๊ฒ์ด ๋ง๋ค.
๋ง์ฝ ํจ๊ณผ๊ฐ ์๋๋ผ๋, ๊ทธ๊ฒ ์์ฒด๋ ๊ฒฝํ์ผ๋ก ์์๋๋๊ฒ ์ข๋ค. ์ค๋ฌด์์๋ ๋ฐ๋๋ผ์ธ์ด์๊ธฐ ๋๋ฌธ์, ๊ทธ ์คํจ ๊ฒฝํ๋ ์ถํ ๊ธฐ์ ์ ํ์์์ ์ค์๋ฅผ ์ค์ผ ์ ์๋ ๊ฒฝํ์ด๊ธฐ ๋๋ฌธ์ ์์๋๋๊ฒ ์ข๋ค.
๊ณผ๊ฑฐ ์ฝ๋ ๋ฐ ์ฃผ์๋ ์ฐ์ ๋ชจ๋ ๋จ๊ฒจ๋๋๊ฒ ์ข๋ค. ์คํฐ๋ ๊ณผ์ ์ ํ๋ก์ ํธ๊ธฐ ๋๋ฌธ์, ๋ธ๋์น๋ ์ญ์ ํ ํ์๋ ์๋ค.
๋ฐฐํฌ ์ต์ข
๋ณธ๋ง ๊ฐ์ฅ ๊น๋ํ๊ฒ release ํ๊ทธํ ์์ผ๋์ ํ์๋ ์๋ค.
Q. ํ์ฌ ๊ตฌํ๋ ์ฑ
๋์ถ/์์ฝ ํ๋ก์ธ์ค๋ ์๋์ ๊ฐ์ต๋๋ค.
๋์ถ : ๋ก๊ทธ์ธํ ์ ์ ๊ฐ ์ง์ ๋ฒํผ์ ๋๋ฌ ์ฑ
์ ๋์ถ/๋ฐ๋ฉํ๋ ๋ฐฉ์
์์ฝ : ๋์ถ๋ ์ฑ
์ด ๋ฐ๋ฉ๋๋ ์ฆ์ ์ฒซ๋ฒ์งธ ์์ฝ์๊ฐ ์๋์ผ๋ก ์ฑ
์ ๋์ถ
ebook์ ๋์ถํ๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ ์ ๋ฐฉ์์ ์ค๋ฌผ ์ฑ
์ ์ฌ์ฉํ๋ ๋์๊ด ์๋น์ค์๋ ๋ถ์ ํฉํ๋ค๊ณ ์๊ฐ๋ฉ๋๋ค.
ํ์ต์ฉ ํ๋ก์ ํธ๋ก์ ์์ ๋์ถ/์์ฝ ํ๋ก์ธ์ค๋ฅผ ์ ์งํด๋ ์๊ด์๋์ง, ํน์ ์ค์ ์๋น์ค์ ๋์
๊ฐ๋ฅํ๋๋ก ํ๋ก์ธ์ค๋ฅผ ๋ฐ๊ฟ์ผ ํ๋์ง ๋ฌธ์๋๋ฆฝ๋๋ค.
[Spring][258][3์ฃผ์ฐจ ๋ฉํ ๋ง] ์ง๋ฌธ๋ด์ฉ ์ ๋ฆฌ์ ๋ต๋ณ

[Spring][258] ํธ๋ฌ๋ธ์ํ
: ํ๋ก๋์์์ ๋ฉ์์ง๋ฅผ ์ ์กํ๋ ๊ณผ์ ์์ ์ญ์ง๋ ฌํ ๋ฌธ์ ๋ฐ์

[Spring][258] Kafka๋ฅผ ํ์ฉํ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ: ์์คํ
๊ตฌ์กฐ ๋ฐ ๊ฐ์ ๋ฐฉํฅ ๊ณ ์ฐฐ

[Spring][258] ์นดํ์นด ๊ธฐ์ด ๊ตฌํ

[Spring][258] ์นดํ์นด๋ฅผ ํตํ ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ ๊ณ ๋ฏผ
๊ณตํต - Controller, ๊ฒ์ ํค์๋์ ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ด์ ๋ธ ๊ฐ์ฒด
์ฐ์ ๊ณตํต์ ์ผ๋ก ์ ์ฉ๋ ์ปจํธ๋กค๋ฌ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Controller
@RequiredArgsConstructor
public class BooksViewController {
private final AdminCategoriesService adminCategoriesService;
private final AdminBooksService adminBooksService;
@GetMapping("/admin/booksManage")
public String adminBooksManageView( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, // ํ์ฌ ํ์ด์ง, ํ์ด์ง ํฌ๊ธฐ
@RequestParam(defaultValue = "bookId") String sort, @RequestParam(defaultValue = "ASC") String direction, // ์ ๋ ฌ ๊ธฐ์ค, ์ ๋ ฌ ๋ฐฉํฅ
@RequestParam(value = "keyword", required = false) String keyword, // ๊ฒ์ ํค์๋
@AuthenticationPrincipal UserDetailsImpl userDetails, Model model) {
long startTime = System.currentTimeMillis();//์คํ์๊ฐ ์ธก์
Sort.Direction sortDirection = Sort.Direction.fromString(direction);
PageRequest pageRequest = PageRequest.of(page, size, sortDirection, sort);
BooksPageResponseDto booksPageResponseDto =
adminBooksService.findBooksWithPaginationAndSearching(userDetails.getUser(), keyword, pageRequest);
model.addAttribute("books", booksPageResponseDto.getAdminBooksResponseDtos());
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", booksPageResponseDto.getTotalPages());
long endTime = System.currentTimeMillis();
long durationTimeSec = endTime - startTime;
System.out.println(durationTimeSec + "m/s"); // ์คํ์๊ฐ ์ธก์
return "admin/booksManage";
}
ํ๋ก ํธ์๋๋ก๋ถํฐ keyword ๋ฅผ QueryString์ผ๋ก โฆ?keyword=abc์ ๊ฐ์ ํํ๋ก HTMLํผ์ผ๋ก๋ถํฐ ํน์ ๊ฐ๋ค์ ์ถ์ถ ํ ์ ์์ผ๋ฉฐ, HTTP์์ฒญ์ ๋ด์ ์ปจํธ๋กค๋ฌ๋ก ๋ฌธ์์ด์ ์ ๋ฌ ํ ์ ์์ต๋๋ค.
@RequestParam(value = "keyword", required = false) String keyword, // ๊ฒ์ ํค์๋
ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํด์๋ ์์ JPA๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด Total Count๋ฅผ ๋ฝ์์์ ํ์ฌ ๋ด๊ฐ ๋ช๋ฒ์งธ ํ์ด์ง์ธ์ง๋ฅผ ๊ณ์ฐํด์ ํ์๋ฅผ ํด์ฃผ์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ฐ ๊ณ์ฐ์ ๋ชจ๋ ์ง์ ๊ณ์ฐํ๋ ๋ฉ์๋, ๋ณ์๋ฅผ ์ง์ ์์ฑํ๊ณ ๋งค๊ฐ๋ณ์๋ก Service์ ์ ๋ฌํ๊ฒ ๋๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ฒ ๋ฉ๋๋ค!ย ํ์ด์ ๋ธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ ์ถ๊ฐ๋์ด์ผ ํ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// ์ง์ offset๊ณผ limit ๊ณ์ฐ
int offset = page * size;
int limit = size;
// ์ ์ฒด ํ์ด์ง ์ ๊ณ์ฐ ๋ฉ์๋
private int calculateTotalPages(long totalCount, int pageSize) {
return (int) Math.ceil((double) totalCount / pageSize);
}
BooksPageResponseDto booksPageResponseDto =
adminBooksService.findBooksWithPaginationAndSearching(userDetails.getUser(), keyword, offset, limit, sortDirection, sort);
์ด ๋ณต์กํ ์ฝ๋๋ฅผ ํด๊ฒฐํด์ฃผ๊ธฐ ์ํด ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ํ์ด์ง๊ณผ ์ ๋ ฌ์ ํ์คํ ํด๋์์ต๋๋ค.
[Spring][258] JPA โ JPQL โ QueryDSL ๋ณํ๊ณผ์

[Spring][258] ํ๋ก์ ํธ ์ฑ๋ฅ ๊ฐ์ : ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ด์ง์์ ์ฌ๋ผ์ด์ค๋ก์ ์ ํ
์ด์ ๊ธ์์ ์ฐ๋ฆฌ๋ ์ธ๋งํฌ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด์์ต๋๋ค.
๊ทธ๋ฌ๋ ๋์์ฑ ์ ์ด์ ๋ํ ํ๊ตฌ๋ ์ฌ๊ธฐ์ ๊ทธ์น์ง ์๊ณ , ์ด๋ฒ ๊ธ์์๋ ๋ฎคํ
์ค(Mutex)๋ฅผ ํ์ฉํ์ฌ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ณด๋ ค๊ณ ํฉ๋๋ค.
๋ฎคํ ์ค๋?
๋ฎคํ
์ค(Mutex)๋ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ๊ณต์ ์์์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง์์ฃผ๋ ๋๊ธฐํ ๋ฉ์ปค๋์ฆ์
๋๋ค.
๋ฎคํ
์ค๋ ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง ๊ณต์ ์์์ ์ ๊ทผํ ์ ์๊ฒ ํ์ฌ ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์๋๋ก ๋์์ค๋๋ค.
๋ฎคํ ์ค๋ฅผ ์ฌ์ฉํ ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ ๊ณผ์
[Spring][258] Spring ๋ฎคํ
์ค๋ฅผ ํ์ฉํ ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ
๊ฒ์ ๊ตฌํ ํ ๋๋ฆฐ ๊ฒ์ ์๋
โข
๋์
์ด์
โข
๋ฌธ์ ์ํฉ
โข
ํด๊ฒฐ ๋ฐฉ์ ์๊ฒฌ
โข
์์ฌ ๊ฒฐ์
๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ค์ ๋ํ๋ ๋ [ํ์ด์ง] ์์ฒด๊ฐ ์ฑ๋ฅ์ ์ข์ฐํ๋ ๋ฌธ์
โข
๋์
์ด์
[Spring][258] ๊ฒ์๊ด๋ จ ๋ฌธ์ ํด๊ฒฐ ๋ฐ ์ฑ๋ฅ ๊ฐ์ ๋ฐฉ์ ์์ง
1.
๋ฝ๊ณต์ ์์์ ์ ๊ทผํ ๋ ๊ทธ ์์์ ์ฌ์ฉํ๋ ์ค๋ ๋๋ง์ด ์ ๊ทผํ๋๋ก ์ ํํ๋ค.๋ค๋ฅธ ์ค๋ ๋๋ค์ ๋๊ธฐํ๋ค.
โข
๋ฎคํ
์ค(Mutex), ์ธ๋งํฌ์ด(Semaphore)
1.
ํธ๋์ญ์
2.
๋ฐ๋๋ฝ ํํผ
[Spring][258] ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ ๋ฐ ์ฑ๋ฅ ๊ฐ์ ๋ฐฉ์ ์์ง

[Spring][258] ํ๋ก์ ํธ ์ฑ๋ฅ ๊ฐ์ : ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ด์ง์์ ์ฌ๋ผ์ด์ค๋ก์ ์ ํ
์ธ๋งํฌ์ด๋?
์ธ๋งํฌ์ด๋ ๋ค์ค ์ค๋ ๋ ํ๋ก๊ทธ๋๋ฐ์์ ๋์์ ๊ณต์ ์์์ ์ ๊ทผํ๋ ์ค๋ ๋์ ์๋ฅผ ์ ํํ์ฌ ๋์์ฑ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋ณ์๋ ์ถ์ ๋ฐ์ดํฐ ํ์
์
๋๋ค.
์ธ๋งํฌ์ด๋ ํน์ ํ ์์ ํ ํฐ์ ๊ฐ์ง๋ฉฐ, ์ค๋ ๋๊ฐ ์ธ๋งํฌ์ด๋ฅผ ํ๋ํ๋ ค๋ฉด ํ ํฐ์ ์ป์ด์ผ ํฉ๋๋ค.
ํ ํฐ์ด ์๋ค๋ฉด, ์ค๋ ๋๋ ๋๊ธฐ ์ํ์ ๋ค์ด๊ฐ๋๋ค.
์ธ๋งํฌ์ด๋ ์ฃผ๋ก ๋ ๊ฐ์ง ์ข
๋ฅ๊ฐ ์์ต๋๋ค
์ด์ง ์ธ๋งํฌ์ด์ ์นด์ดํ
์ธ๋งํฌ์ด์ด์ง ์ธ๋งํฌ์ด๋ 0 ๋๋ 1์ ๊ฐ์ ๊ฐ์ง๋ฉฐ, ์ํธ ๋ฐฐ์ ๋ฅผ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
์นด์ดํ
์ธ๋งํฌ์ด๋ 0 ์ด์์ ๊ฐ์ ๊ฐ์ง ์ ์์ผ๋ฉฐ, ์ ํ๋ ์์ ์์์ ๋ํ ์ ๊ทผ์ ๊ด๋ฆฌํฉ๋๋ค.
ํ์ ์ด ๊ธ์์ ๋์์ฑ ๋ฌธ์ ํด๊ฒฐ์ ์ํด์ ์ด์ง ์ธ๋งํฌ์ด๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
๋ฌธ์ ์ํฉ
[Spring][258] Spring ์ธ๋งํฌ์ด๋ฅผ ํตํ ๋์์ฑ ์ค๋ฅ ํด๊ฒฐ
๋ฌธ์
Spring Boot์์ Controller ๊ณ์ธต์ ๋ฉ์๋๋ฅผ ํ
์คํธํ๋ ์ค NullPointerException์ด ๋ฐ์ํ๋ค.
@RequestParam์ ์ฌ์ฉํ์ฌ ์์ฒญ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์์ค๋ ๊ณผ์ ์์ ํ๋ผ๋ฏธํฐ์ ๊ธฐ๋ณธ๊ฐ์ด null๋ก ์ค์ ๋์ด ์์๊ณ , ์ด๋ก ์ธํด ํ
์คํธ ์ค์ null ๊ฐ์ด ๋ฉ์๋๋ก ์ ๋ฌ๋์๋ค.
Mockito๋ฅผ ์ฌ์ฉํ์ฌ ์๋น์ค ๊ณ์ธต์ ๋ฉ์๋๋ฅผ ๋ชจ์(Mock)ํ ๋ any(ํด๋์ค.class) ํํ๋ก ์์ฑํ์์ง๋ง, ์ค์ ๋ก๋ null ๊ฐ์ด ์ ๋ฌ๋์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๋ฌธ์ ๋ถ์
@RequestParam์ ๊ธฐ๋ณธ๊ฐ์ด null๋ก ์ค์ ๋์ด ์๊ณ , Mockito๋ฅผ ์ฌ์ฉํ์ฌ ์๋น์ค ๊ณ์ธต์ ๋ฉ์๋๋ฅผ ๋ชจ์ํ ๋ any(ํด๋์ค)๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, null ๊ฐ์ ๋ฐ์๋ค์ผ ์ ์์๋ค.
[Spring][258] ํธ๋ฌ๋ธ ์ํ
: Spring Boot ํ
์คํธ: @RequestParam์ ๊ธฐ๋ณธ๊ฐ๊ณผ Mockito any() ๋ฉ์๋ ์ฌ์ฉ ์ ์ฃผ์์

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: `BookServiceTest`์์ `NullPointerException` ๋ฐ์ ๋ฌธ์
Q. ํ์ด์ง ์ฒ๋ฆฌ์ ๊ด๋ จ๋ ์ฝ๋ ์ง๋ฌธ
์ฐ์ ํ์ด์ ๋ธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋๊ฒ์ด ํ์คํ๋์๋ค๊ณ ํด์ ํ์ด์ ๋ธ ๊ฐ์ฒด๋ก ํ์ด์ง์ ๊ตฌํํด๋ณด์์ต๋๋ค.
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
Q. ํ์ด์ง์ ๋ํด ์๋์ ๊ฐ์ด ์ ๊ทผํด๋ณด์์ต๋๋ค.
์ฑ
๋๋ ์ด๋ฒคํธ์ ๋์ ์ฌ์ด์ 1๋N์ ๊ด๊ณ๊ฐ ์
๋๋ค.
์ฒ์์๋ ์ฑ
๋๋ ์ด๋ฒคํธ์์ ๋์๋ฅผ ํจ์น ์กฐ์ธํ์ฌ ํ์ด์ง์ ์๋ํ๋๋ฐ ๋ฉ๋ชจ๋ฆฌ์์ ํ์ด์ง์ด ๋์ด ์ค์๋์ ์ํ์ด ์๋ค๊ณ ์๊ฒ ๋์์ต๋๋ค.
๊ทธ๋์ ๋์ ์ชฝ(N)์์ ํจ์น ์กฐ์ธํ์ฌ ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์งํํ์ต๋๋ค.
์ด๋ฐ ๋ฐฉ์์ด ์ณ์ ์ ๊ทผ์ธ์ง ๊ถ๊ธํ๊ณ , ์ฑ
๋๋ ์ด๋ฒคํธ(1)์์ JPQL์ ์ฌ์ฉํ์ฌ ์ง์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํด ํ์ด์ง์ ์๋ํ ๋ ๋ฐ์ํ๋ ๊ตฌ์ฒด์ ์ธ ๋ฌธ์ ์ ์ด ๋ฌด์์ธ์ง ์๋ ค์ฃผ์ค ์ ์์๊น์?
๊ทธ๋ฆฌ๊ณ ๋์ ์ชฝ(N)์์ ํ์ด์ง์ ์ฒ๋ฆฌํ ๋ ๊ฒน์น๋ ๋ฌธ์ ๋ ๋ค๋ฅธ ์ด์๊ฐ ์์ ์ ์๋์ง๋ ์ฌ์ญค๋ณด๊ณ ์ถ์ต๋๋ค
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
์ฐ์ ํ์ด์ง ์ฒ๋ฆฌ๋ 1์ด๋ N์ด๋ ์ด๋์์๋ ํ์ํ๋ค. ์ฟผ๋ฆฌ๋ฌธ์ด ์ด๋ค ์์ผ๋ก ์๋๋๋์ง ๋ณด๊ณ ํ์
ํ ํ์๊ฐ ์๋ค.
ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ํ๋ ๋์์ ์ง์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ด ๋ง๋ค.
Q. ์ฝ๋ ๋๋ฒ๊น
์ค ํน์ ์ด์์ ์ง๋ฉดํ์ต๋๋ค. ๋๋ฒ๊น
์ ํตํด ํ๋ก๊ทธ๋จ์ ํ๋ฆ์ ๋ฉ์ท์์๋ ๋ถ๊ตฌํ๊ณ ๋๋ฒ๊ฑฐ๋ฅผ ํตํด ํด๋น ๊ฐ์ฒด๋ฅผ ์กฐํํ๋ ์ค์ ์ฟผ๋ฆฌ๊ฐ ์ ๋ฌ๋์์ต๋๋ค.
๊ทธ ๊ฒฐ๊ณผ ๋๋ฒ๊ฑฐ์์์ ์ฝ์์์์ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ ํผ๋์ ๊ฒช์์ต๋๋ค.
์ ๊ฐ ๋ฌธ์ ๋ฅผ ํ์ํ ๊ฒฐ๊ณผ, Java์ ORM ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ ๋ ๋๋ฒ๊ฑฐ๋ก ๊ฐ์ฒด์ ์ํ๋ฅผ ํ์ธํ๋ ค๊ณ ํ ๋ ๊ทธ ๊ฐ์ฒด์ ์ค์ ๋ ์ง์ฐ ๋ก๋ฉ์ด ๋์ํ๊ฒ ๋์ด ์ด๋ฌํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
์ด๋ฅผ "๋๋ฒ๊ฑฐ ์ง์ฐ๋ก๋ฉ ํธ๋ฉ"์ด๋ผ๊ณ ํ๋ ๊ฒ ๊ฐ๋๊ตฐ์.
A. ๋ฉํ ๋๋ต๋ณ์ ๋ฆฌ!
๊ฐ์ฒด์ ์ด๋ค ๋ฐ์ดํฐ๊ฐ ์์ ์ง ๋๋ฒ๊ฑฐ ์ปค์๊ฐ ๋์ด๊ฐ๋ ๋ฐ์ํ๋ ๋ฌธ์ ์ธ๋ฐ,
์ค์ ์ฝ๋ ์คํ๊ณผ ๋๋ฒ๊น
ํ๊ฒฝ์ ๋ค๋ฅผ ์ ์๋ค. ๋ฌธ์ ๊ฐ ์๋๋ผ, ๋๋ฒ๊ฑฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด์ ์ํ๋ฅผ ํ์ธํ๋ฉด์ ๋ฐ์ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๊ฐ ์์ ์ ์์์ ์ฃผ์ํด์ผ ํ๋ฉฐ, ์ด๋ก ์ธํ ๋ถ์์ฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ถฉ๋ถํ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
[Spring][258] [2์ฃผ์ฐจ ๋ฉํ ๋ง] ์ง๋ฌธ๋ด์ฉ ์ ๋ฆฌ์ ๋ต๋ณ
๋ฌธ์ ์ํฉ
1.
JPA ํ์ด์ง ์ฒ๋ฆฌ ๋ฌธ์ BookDonationEvent์ Book ๊ฐ์ 1๋๋ค ๊ด๊ณ์์ BookDonationEvent ์ค์ฌ์ ํ์ด์ง ์ฟผ๋ฆฌ ์คํ ์ ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์ง ๋ชปํ๋ ํ์์ด ๋ฐ์ํ๋ค.
์ปค์คํ
์ปค๋ฆฌ๋ฅผ ์์ฑํ์์ง๋ง ์ ์์ ์ผ๋ก ๋์ํ์ง ์๋๋ค.
๋์์ event๊ฐ 1๋ ๋ค ๊ด๊ณ๋ผ์ ์ ์์ ์ผ๋ก ์๋ํ์ง ์๋ ๊ฑธ๋ก ์ถ์ ๋๋ค.
@GetMapping("{donationId}")
public String bookApplyDonationEventPage(Model model, @PathVariable Long donationId) {
BookDonationEvent bookDonationEvent = bookDonationEventRepository.findById(donationId).orElseThrow(
() -> new IllegalArgumentException("ํด๋น ์ด๋ฒคํธ๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค.")
);
List<Book> books = bookDonationEvent.getBooks().stream().filter(book -> book.getBookStatus().equals(BookStatusEnum.DONATION)).toList();
List<BookResponseDto> bookResponseDtos = books.stream()
.map(BookResponseDto::new)
.toList();
BookDonationEventResponseDto bookDonationEventResponseDto = new BookDonationEventResponseDto(bookDonationEvent);
model.addAttribute("bookDonationEvent", bookDonationEventResponseDto);
model.addAttribute("books", bookResponseDtos);
return "/users/bookApplyDonation";
}
@Query(value = "select bde from BookDonationEvent bde" +
" join fetch bde.books book" +
" where bde.id = :donationId and book.bookStatus = :status")
Page<BookDonationEvent> findPageByDonationId(@Param("donationId") Long donationId, @Param("status") BookStatusEnum status, Pageable pageable);
์์ธ ๋ถ์
ํ์ด์ง ์ฒ๋ฆฌ์ ๋ณต์ก์ฑ
JPA์ ํ์ด์ง ์ฒ๋ฆฌ๋ ๋
ผ๋ฆฌ์ ์ผ๋ก ๊ฐ๋จํ์ง๋ง join fetch๋ฅผ ์ฌ์ฉํ๋ฉด์ ๋ฐ์ํ๋ ๋ฌธ์ ๋ ๊ฐ BookDonationEvent๊ฐ ์ฌ๋ฌ Book์ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ผ์ ํ BookDonationEvent ๋ด๋ถ์ ์ฌ๋ฌ Book๋ค์ด ํ์ด์ง ์ฒ๋ฆฌ์ ๋์์ด ๋๋ฉด์ ์ํ๋ ํ์ด์ง ๊ฒฐ๊ณผ๋ฅผ ์ป๊ธฐ ์ด๋ ค์ ๋ ๊ฒ ๊ฐ๋ค.
ํด๊ฒฐ
์ ์ ํ ํ์ด์ง ์ฒ๋ฆฌ
[Spring][258] ํธ๋ฌ๋ธ ์ํ
: JPA ํ์ด์ง ์ฒ๋ฆฌ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: ๋๋ฒ๊ฑฐ์์์ ์ง์ฐ ๋ก๋ฉ(Lazy Loading) ํธ๋ฉ

[Spring][258] ํธ๋ฌ๋ธ ์ํ
: Spring JPA์์ ํ์ด์ง ์ฟผ๋ฆฌ ์ค๋ฅ ํด๊ฒฐ


