π[λΆμμΆκ°] &β‘οΈ[μ±λ₯ ν₯μ] Prometheus & Grafana λͺ¨λν°λ§ νκ²½ κ΅¬μΆ & μ±λ₯ ν₯μ - #101
Conversation
μ½λ μμ FIXME 리μ€νΈ μνν κ².
λ‘μ§ μΆκ° ꡬνμ€
IntelliJ μλ κ΅¬μ± ν΄λ μΆκ°
Prometheusλ₯Ό ν΅ν΄ SpringBoot μλ²μ Redis μλ² λ°μ΄ν° μμ§νμ¬, Grafanaλ‘ λμ보λ ꡬν(μκ°ν)
DBλ° ElasticSearch μλ²μ λν μ κ·Ό νμλ₯Ό μ€μ΄κ³ μ±λ₯μ λΉμ½μ μΌλ‘ ν₯μμν΄
μ€λ³΅ μ κ±° λ‘μ§μμ SQLμ INDEXλ₯Ό μ μ©λ°μ μ μλλ‘ μμ μ€λ³΅ μ κ±° μ λ³΅ν© INDEXλ₯Ό μ°Έμ‘°νμ¬ μ±λ₯μ΄ ν₯μ λ¨
Walkthroughμ΄λ² λ³κ²½ μ¬νμ Redis κΈ°λ° μΊμ λμ , Prometheus λ° Grafana λͺ¨λν°λ§ μΈνλΌ μΆκ°, λ΄μ€ λ°μ΄ν°λ² μ΄μ€ μΈλ±μ€ μ΅μ ν, μΌλΆ λΉμ¦λμ€ λ‘μ§ κ°μ , κ·Έλ¦¬κ³ μΊμ λ° λͺ¨λν°λ§ κ΄λ ¨ μ€μ νμΌ μΆκ° λ° μμ μ΄ ν¬ν¨λ©λλ€. λν, JWT νν° μμΈ κ²½λ‘μ μ€λ³΅ λ΄μ€ μμ 쿼리 λ‘μ§μ΄ μ‘°μ λμμ΅λλ€. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant HotTopicService
participant RedisCache
participant DB
participant ES
Client->>HotTopicService: getHotTopicList()
HotTopicService->>RedisCache: μΊμ μ‘°ν(hottopic:daily)
alt μΊμ HIT
RedisCache-->>HotTopicService: μΊμλ 리μ€νΈ λ°ν
HotTopicService-->>Client: 리μ€νΈ λ°ν
else μΊμ MISS
HotTopicService->>DB: μ΄μ μ HotTopic 10κ° μ‘°ν
HotTopicService->>RedisCache: μΊμ μ μ₯(λ§λ£: λ΄μΌ 0μ)
HotTopicService-->>Client: 리μ€νΈ λ°ν
end
Client->>HotTopicService: getNewsList(keyword, size)
HotTopicService->>RedisCache: μΊμ μ‘°ν(hottopic:news:{keyword})
alt μΊμ HIT
RedisCache-->>HotTopicService: μΊμλ λ΄μ€ 리μ€νΈ λ°ν
HotTopicService-->>Client: 리μ€νΈ λ°ν
else μΊμ MISS
HotTopicService->>ES: ν€μλ λ΄μ€ κ²μ
HotTopicService->>RedisCache: μΊμ μ μ₯(λ§λ£: λ΄μΌ 0μ)
HotTopicService-->>Client: 리μ€νΈ λ°ν
end
sequenceDiagram
participant BatchJob
participant DB
BatchJob->>DB: μ€λ³΅ λ΄μ€ λ° "[μ보]" ν¬ν¨ λ΄μ€ μμ 쿼리 μ€ν
Note right of DB: published_at λ²μ λ° title 쑰건 λμ μ μ©
sequenceDiagram
participant User
participant SettingController
participant SettingService
participant UserRepository
User->>SettingController: saveSetting(settingDTO)
SettingController->>SettingService: saveSetting(settingDTO)
SettingService->>UserRepository: findById(userId)
alt User μμ
SettingService-->>SettingController: SettingException(USER_NOT_FOUND)
else User μμ
SettingService->>SettingService: isSettingLimitExceeded(user)
alt μ ν μ΄κ³Ό
SettingService-->>SettingController: SettingException(SETTING_LIMIT_EXCEEDED)
else μ ν λ―Έμ΄κ³Ό
SettingService->>SettingService: Setting μν°ν° μμ± λ° μ¬μ©μ μ°κ²°
SettingService-->>SettingController: μλ£
end
end
β¨ Finishing Touches
πͺ§ TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
π§Ή Nitpick comments (6)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Controller/SettingController.java (1)
75-75: TODO νλͺ©μ λν λμ μ μμ€μ κ°μ μ ν(3κ°) κ²μ¦ λ‘μ§μ΄ μμ§ κ΅¬νλμ§ μμμ΅λλ€. μλΉμ€ λ μ΄μ΄μμ κ΄λ ¨ κ²μ¦ λ©μλκ° μ€λΉλμ΄ μλ κ²μΌλ‘ 보μ λλ€.
μ΄ κ²μ¦ λ‘μ§μ ꡬννκ±°λ μλ‘μ΄ μ΄μλ₯Ό μμ±νμ¬ μΆμ νλ κ²μ λμλ릴κΉμ?
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java (1)
246-253: μ€μ μ ν κ²μ¦ λ‘μ§ κ΅¬ν νμμ€μ κ°μ μ ν κ²μ¦μ μν λ©μλκ° μ€ν μΌλ‘λ§ κ΅¬νλμ΄ μμ΅λλ€. μ€μ κ²μ¦ λ‘μ§μ΄ νμν©λλ€.
λ€μκ³Ό κ°μ ꡬνμ μ μλ립λλ€:
private boolean isSettingLimitExceeded(User user) { long activeSettingsCount = settingRepository.countActiveSettingsByUser(user, LocalDateTime.now()); return activeSettingsCount >= 3; }μ΄ κ΅¬νμ μν΄μλ SettingRepositoryμ
countActiveSettingsByUserλ©μλλ₯Ό μΆκ°ν΄μΌ ν©λλ€. ꡬνμ λμλ릴κΉμ?SpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/HotTopic_Description (1)
7-7: μ μ©ν μλ¦Ό μ½λ©νΈμ λλ€.FIXME μ½λ©νΈλ₯Ό ν΅ν΄ ν₯ν κ°λ°μμ μ²λ¦¬ν΄μΌ ν μꡬμ¬νμ λͺ νν λ¬Έμννμ΅λλ€.
μ΄ μμ μ μΆμ νκΈ° μν μ΄μλ₯Ό μμ±νλ κ²μ κ³ λ €ν΄λ³΄μκ² μ΅λκΉ? λμμ΄ νμνμλ©΄ λ§μν΄ μ£ΌμΈμ.
monitoring/prometheus.yml (1)
25-25: νμΌ λμ κ°ν λ¬Έμλ₯Ό μΆκ°νμΈμ.YAML νμΌ νμ€μ λ°λΌ νμΌ λμ κ°ν λ¬Έμκ° νμν©λλ€.
- - targets: [ 'redis-session2-exporter:9121' ] + - targets: [ 'redis-session2-exporter:9121' ] +docker-compose.yml (1)
167-197: Redis μ΅μ€ν¬ν° ꡬμ±μ΄ μ°μν©λλ€.λͺ¨λ Redis μΈμ€ν΄μ€μ λν λͺ¨λν°λ§ μ€μ κ³Ό ν¬νΈ μΆ©λ λ°©μ§κ° μ ꡬμ±λμ΄ μμ΅λλ€.
μ νμ κ°μ μ¬ν: redis-session1-exporterμ redis-session2-exporterμλ ν΄λΉ Redis μλΉμ€μ λν μμ‘΄μ±μ μΆκ°νλ κ²μ κ³ λ €ν΄λ³΄μΈμ:
redis-session1-exporter: image: oliver006/redis_exporter container_name: redis-session1-exporter command: - '--redis.addr=redis://redis-session1:6379' ports: - "9122:9121" + depends_on: + - redis-session1 networks: - backendSpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/service/HotTopicService.java (1)
155-176: λ΄μ€ 리μ€νΈ μΊμ± ꡬνμ΄ μΌκ΄μ± μκ² μ ꡬνλμμ΅λλ€.ν€μλ κΈ°λ° μΊμ ν€ μ λ΅κ³Ό μ±λ₯ λ‘κΉ μ΄ ν«ν ν½ μΊμ±κ³Ό μΌκ΄λκ² μ μ©λμμ΅λλ€.
μ νμ κ°μ μ¬ν: ν€μλ κΈ°λ° λ΄μ€ κ²μ κ²°κ³Όμ μΊμ λ§λ£ μ λ΅μ ν«ν ν½κ³Ό λ€λ₯΄κ² μ€μ νλ κ²μ κ³ λ €ν΄λ³΄μΈμ. μλ₯Ό λ€μ΄, λ΄μ€ κ²μ κ²°κ³Όλ λ μ§§μ TTLμ κ°μ§ μ μμ΅λλ€:
- long ttlSeconds = Duration.between( - LocalDateTime.now(), - LocalDate.now().plusDays(1).atStartOfDay() - ).getSeconds(); + // λ΄μ€ κ²μ κ²°κ³Όλ 1μκ° μΊμ + long ttlSeconds = Duration.ofHours(1).getSeconds();
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (16)
.gitignore(1 hunks)SpringBoot/build.gradle(1 hunks)SpringBoot/database/init.sql(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/HotTopic_Description(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/service/HotTopicService.java(4 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Controller/SettingController.java(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java(2 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Global/Config/RedisConfig.java(2 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/ErrorCode.java(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Global/News/Batch/listener/BatchJobCompletionListener.java(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Global/News/ElasticSearch/service/NewsEsService.java(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/NewsDeliverApplication.java(1 hunks)SpringBoot/src/main/resources/application.properties(2 hunks)docker-compose.yml(1 hunks)monitoring/prometheus.yml(1 hunks)
π§° Additional context used
πͺ YAMLlint (1.37.1)
monitoring/prometheus.yml
[error] 25-25: no new line character at the end of file
(new-line-at-end-of-file)
π Additional comments (22)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/ErrorCode.java (1)
51-51: μλ‘μ΄ μλ¬ μ½λ μΆκ° μΉμΈμ€μ κ°μ μ ν μ΄κ³Όμ λν μλ‘μ΄ μλ¬ μ½λκ° μ μ ν μΆκ°λμμ΅λλ€. λͺ λͺ κ·μΉμ μ λ°λ₯΄κ³ μμΌλ©°, HTTP μν μ½λλ μ μ ν©λλ€.
.gitignore (1)
2-2: μ’μ λ³κ²½μ¬νμ λλ€.IntelliJ IDEA νλ‘μ νΈ νμΌμ .gitignoreμ μΆκ°νλ κ²μ νμ€μ μΈ κ΄λ‘μ λλ€. μ΄λ₯Ό ν΅ν΄ IDE μ€μ νμΌμ΄ μ μ₯μμ 컀λ°λλ κ²μ λ°©μ§ν μ μμ΅λλ€.
SpringBoot/src/main/java/Baemin/News_Deliver/NewsDeliverApplication.java (2)
5-5: μ μ ν import μΆκ°μ λλ€.Springμ μΊμ± κΈ°λ₯μ νμ±ννκΈ° μν μ¬λ°λ₯Έ importμ λλ€.
12-12: μΊμ± κΈ°λ₯ νμ±νκ° μ¬λ°λ₯΄κ² ꡬνλμμ΅λλ€.@EnableCaching μ΄λ Έν μ΄μ μ ν΅ν΄ Springμ μΊμ± μΆμνλ₯Ό νμ±ννμ΅λλ€. μ΄λ Redis μΊμ ꡬνκ³Ό μΌμΉνλ©° μ¬λ°λ₯Έ μμΉμ λ°°μΉλμμ΅λλ€.
SpringBoot/src/main/resources/application.properties (3)
15-15: Redis μΊμ νμ μ€μ μ΄ μ¬λ°λ₯΄κ² ꡬμ±λμμ΅λλ€.Spring Bootμ μΊμ μΆμνλ₯Ό μν μ μ ν Redis μ€μ μ λλ€.
18-18: Redis νμμμ μ€μ κ²ν κ° νμν©λλ€.24μκ°μ Redis νμμμμ λ§€μ° κΈ΄ μκ°μ λλ€. μ΄ μ€μ μ΄ μ ν리μΌμ΄μ μ μꡬμ¬νκ³Ό μΌμΉνλμ§ νμΈν΄μ£ΌμΈμ.
λ€μ μ¬νλ€μ κ²ν ν΄λ³΄μΈμ:
- μΊμ λ°μ΄ν°μ νΉμ±κ³Ό μ ν¨ κΈ°κ°
- λ©λͺ¨λ¦¬ μ¬μ©λμ λν μν₯
- λ°μ΄ν° μΌκ΄μ± μꡬμ¬ν
89-90: Prometheus λͺ¨λν°λ§ μ€μ μ΄ μ μ νκ² κ΅¬μ±λμμ΅λλ€.health, info, prometheus μλν¬μΈνΈλ₯Ό λ ΈμΆνκ³ μ ν리μΌμ΄μ λ³ λ©νΈλ¦ νκ·Έλ₯Ό μΆκ°ν κ²μ μ’μ λͺ¨λν°λ§ μ€μ μ λλ€.
SpringBoot/build.gradle (1)
85-91: λͺ¨λν°λ§ λ° μΊμ± μμ‘΄μ± μΆκ°κ° μ μ ν©λλ€.Prometheus λͺ¨λν°λ§κ³Ό Redis μΊμ± κΈ°λ₯μ μν μμ‘΄μ±μ΄ μ¬λ°λ₯΄κ² μΆκ°λμμ΅λλ€. Spring Bootμ μμ‘΄μ± κ΄λ¦¬λ₯Ό νμ©νμ¬ λ²μ μΆ©λ μμ΄ ν΅ν©ν μ μμ΅λλ€.
monitoring/prometheus.yml (1)
1-25: Prometheus μ€μ μ΄ λͺ¨λν°λ§ λͺ©μ μ μ ν©ν©λλ€.Spring Boot μ ν리μΌμ΄μ κ³Ό μ¬λ¬ Redis μΈμ€ν΄μ€μ λν λͺ¨λν°λ§ μ€μ μ΄ μ¬λ°λ₯΄κ² ꡬμ±λμμ΅λλ€. 5μ΄ μ€ν¬λν κ°κ²©μ κ°λ° νκ²½μ μ μ ν©λλ€.
SpringBoot/src/main/java/Baemin/News_Deliver/Global/News/ElasticSearch/service/NewsEsService.java (1)
97-101: SQL 쿼리 μ΅μ νκ° μ±λ₯ ν₯μμ λμμ΄ λ©λλ€.λ μ§ νν°λ§ λ‘μ§μ DATE() ν¨μ μ¬μ©μμ νμμ€ν¬ν λ²μ 쑰건μΌλ‘ λ³κ²½ν κ²μ μ’μ κ°μ μ λλ€. μ΄ λ³κ²½μΌλ‘ μΈν΄
idx_news_published_sectionsλ³΅ν© μΈλ±μ€λ₯Ό ν¨μ¨μ μΌλ‘ νμ©ν μ μμ΄ μΏΌλ¦¬ μ±λ₯μ΄ ν₯μλ©λλ€.SpringBoot/database/init.sql (1)
114-118: λ°μ΄ν°λ² μ΄μ€ μΈλ±μ€ μ΅μ νκ° μ μ€κ³λμμ΅λλ€.μΆκ°λ λ³΅ν© μΈλ±μ€λ€μ΄ 쿼리 μ±λ₯ ν₯μμ ν¨κ³Όμ μ λλ€:
idx_news_published_sections: λ μ§λ³ μΉμ μ‘°ν μ±λ₯ ν₯μidx_news_dup: μ€λ³΅ μμ λ‘μ§μ ROW_NUMBER() μ°μ° μ΅μ νκ° μΈλ±μ€μ μ»¬λΌ μμλ 쿼리 ν¨ν΄μ λ§κ² μ μ ν μ€μ λμμ΅λλ€.
SpringBoot/src/main/java/Baemin/News_Deliver/Global/News/Batch/listener/BatchJobCompletionListener.java (1)
41-53: μ€λ³΅ μμ λ‘μ§ κ°μ μ΄ μ’μ§λ§ μ보 μμ λ²μλ₯Ό νμΈνμΈμ.νμμ€ν¬ν λ²μ νν°λ§μΌλ‘ λ³κ²½ν κ²μ μΈλ±μ€ νμ© μΈ‘λ©΄μμ μ°μν©λλ€. νμ§λ§
[μ보]μμ μ‘°κ±΄μ΄ λ μ§ νν°λ§κ³Ό ORλ‘ μ°κ²°λμ΄ μμ΄ λͺ¨λ λ μ§μ μλ³΄κ° μμ λ μ μμ΅λλ€.μλν λμμ΄ λ§λμ§ νμΈν΄μ£ΌμΈμ. λ§μ½ μ΄μ λ μ§μ μλ³΄λ§ μμ νλ €λ©΄ λ€μκ³Ό κ°μ΄ μμ μ κ³ λ €ν΄λ³΄μΈμ:
- WHERE ( - id IN ( - SELECT id FROM ( - SELECT id, - ROW_NUMBER() OVER (PARTITION BY title, publisher ORDER BY id) AS rn - FROM news - WHERE published_at >= CURDATE() - INTERVAL 1 DAY - AND published_at < CURDATE() - ) t - WHERE t.rn > 1 - ) - OR title LIKE '%[μ보]%' - ); + WHERE id IN ( + SELECT id FROM ( + SELECT id, + ROW_NUMBER() OVER (PARTITION BY title, publisher ORDER BY id) AS rn + FROM news + WHERE published_at >= CURDATE() - INTERVAL 1 DAY + AND published_at < CURDATE() + AND (title LIKE '%[μ보]%' OR 1=1) + ) t + WHERE t.rn > 1 OR title LIKE '%[μ보]%' + );SpringBoot/src/main/java/Baemin/News_Deliver/Global/Config/RedisConfig.java (4)
3-16: νμν μμ‘΄μ± μν¬νΈκ° μ¬λ°λ₯΄κ² μΆκ°λμμ΅λλ€.Jackson μ§λ ¬ν λ° Redis JSON μ리μΌλΌμ΄μ κ΄λ ¨ μν¬νΈκ° μΊμ κΈ°λ₯ ꡬνμ μ μ νκ² μΆκ°λμμ΅λλ€.
69-75: μΊμ μ μ© Redis μ°κ²° ν©ν λ¦¬κ° μ¬λ°λ₯΄κ² ꡬμ±λμμ΅λλ€.μΈμ μ© Redisμ λΆλ¦¬λ μΊμ μ μ© μ°κ²° ν©ν λ¦¬λ‘ κ΄μ¬μ¬ λΆλ¦¬κ° μ λμ΄ μμ΅λλ€.
77-83: ObjectMapper ꡬμ±μ΄ μ°μν©λλ€.Java 8 λ μ§/μκ° νμ μ§μμ μν JavaTimeModule λ±λ‘κ³Ό νμμ€ν¬ν λΉνμ±νκ° μ¬λ°λ₯΄κ² μ€μ λμ΄ μμ΄ μΊμμμ λ μ§ κ°μ²΄μ κ°λ μ±μ΄ ν₯μλ©λλ€.
85-103: μΊμ μ μ© RedisTemplate ꡬμ±μ΄ μ°μν©λλ€.JSON μ§λ ¬νλ₯Ό ν΅ν 볡μ‘ν κ°μ²΄ μΊμ± μ§μκ³Ό λͺ νν Bean μ΄λ¦ μ§μ μ΄ μ λμ΄ μμ΅λλ€. GenericJackson2JsonRedisSerializerλ νμ μ 보λ₯Ό ν¬ν¨νμ¬ μμ ν μμ§λ ¬νκ° κ°λ₯νμ§λ§, μ μ₯ 곡κ°μ λ μ¬μ©νλ€λ μ μ μ°Έκ³ νμΈμ.
docker-compose.yml (3)
134-143: Prometheus μλΉμ€ ꡬμ±μ΄ μ¬λ°λ¦ λλ€.μ€μ νμΌ λ§μ΄νΈμ μꡬ λ³Όλ₯¨ ꡬμ±μ΄ μ μ νκ² λμ΄ μμ΄ λ©νΈλ¦ λ°μ΄ν° μ§μμ±μ΄ 보μ₯λ©λλ€.
145-154: Grafana μλΉμ€ ꡬμ±μ΄ μ μ ν©λλ€.μΉ μΈν°νμ΄μ€ ν¬νΈ λ ΈμΆκ³Ό λμ보λ λ°μ΄ν° μ§μμ±μ μν λ³Όλ₯¨ ꡬμ±μ΄ μ¬λ°λ¦ λλ€.
204-205: λͺ¨λν°λ§ λκ΅¬μ© μꡬ λ³Όλ₯¨μ΄ μ¬λ°λ₯΄κ² μΆκ°λμμ΅λλ€.Prometheusμ Grafanaμ λ°μ΄ν° μ§μμ±μ μν λ³Όλ₯¨ ꡬμ±μ΄ μ μ ν©λλ€.
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/service/HotTopicService.java (3)
51-59: μμ±μ μ£Όμ λ°©μμ΄ μ¬λ°λ₯΄κ² ꡬνλμμ΅λλ€.νΉμ Redis ν νλ¦Ώ λΉ μ£Όμ μ μν @qualifier μ¬μ©μΌλ‘ λͺ μμ μΈ μμ±μ μ£Όμ μ΄ μ μ νκ² μ μ©λμμ΅λλ€.
72-105: ν«ν ν½ μΊμ± ꡬνμ΄ μ°μν©λλ€.μΌμΌ λ°μ΄ν° νΉμ±μ λ§λ μΊμ ν€ μ λ΅, μμ κΉμ§μ TTL κ³μ°, κ·Έλ¦¬κ³ μ±λ₯ λ‘κΉ μ΄ μ ꡬνλμ΄ μμ΅λλ€. νΉν λ€μλ μμ κΉμ§μ TTL κ³μ° λ‘μ§μ΄ μΌμΌ ν«ν ν½μ νΉμ±μ λ§€μ° μ ν©ν©λλ€.
61-61: μΊμ ν€ μ λ΅μ΄ μ μ μλμμ΅λλ€.μμλ‘ μ μλ μΊμ ν€ μ λμ¬λ₯Ό ν΅ν΄ λ€μν μΊμ λ°μ΄ν° νμ μ 체κ³μ μΈ κ΄λ¦¬κ° κ°λ₯ν©λλ€.
| User user = userRepository.findById(settingDTO.getUserId()) | ||
| .orElseThrow(() -> new SettingException(ErrorCode.USER_NOT_FOUND)); | ||
|
|
||
| if (isSettingLimitExceeded(user)) { | ||
| throw new SettingException(ErrorCode.SETTING_LIMIT_EXCEEDED); | ||
| } | ||
|
|
||
| Setting setting = new Setting(); | ||
| setting.setDeliveryTime(settingDTO.getDeliveryTime()); | ||
| setting.setStartDate(settingDTO.getStartDate()); | ||
| setting.setEndDate(settingDTO.getEndDate()); | ||
| setting.setIsDeleted(false); | ||
|
|
||
| User user = userRepository.findById(settingDTO.getUserId()) | ||
| .orElseThrow(() -> new SettingException(ErrorCode.USER_NOT_FOUND)); | ||
| setting.setUser(user); | ||
|
|
There was a problem hiding this comment.
Setting μν°ν° μ μ₯ λλ½ λ¬Έμ
User μ‘°ν λ° κ²μ¦ λ‘μ§μ μ ꡬνλμμ§λ§, Setting μν°ν°μ λͺ
μμ μ μ₯μ΄ λλ½λμμ΅λλ€. setting.getId()λ₯Ό λ°ννλ €λ©΄ λ¨Όμ μν°ν°κ° μ μ₯λμ΄μΌ ν©λλ€.
λ€μκ³Ό κ°μ΄ μμ νμ¬ Setting μν°ν°λ₯Ό μ μ₯νμΈμ:
setting.setUser(user);
+
+settingRepository.save(setting);
saveSettingKeyword(settingDTO, setting);π€ Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java
around lines 64 to 78, the Setting entity is created and populated but not saved
to the database, so calling setting.getId() will not work correctly. Fix this by
explicitly saving the Setting entity using the appropriate repository's save
method after setting its fields and before returning its ID.
| return path.startsWith("/login/oauth2/") | ||
| || path.startsWith("/oauth2/") | ||
| || path.equals("/") | ||
| || path.startsWith("/css/") | ||
| || path.startsWith("/js/") | ||
| || path.startsWith("/images/"); |
There was a problem hiding this comment.
π‘ Verification agent
π§© Analysis chain
JWT νν° μ μΈ κ²½λ‘ λ³κ²½μ κ²μ¦ν΄μΌ ν©λλ€.
/api/auth/ λ° / κ²½λ‘λ₯Ό μ μΈ λͺ©λ‘μμ μ κ±°νμ΅λλ€. μ΄λ μ€μν 보μ λ³κ²½μ¬νμΌλ‘, ν΄λΉ κ²½λ‘λ€μ΄ μ΄μ JWT μΈμ¦μ μꡬνκ² λ©λλ€.
/api/auth/κ²½λ‘κ° λ‘κ·ΈμΈ/νμκ°μ μλν¬μΈνΈλ₯Ό ν¬ν¨νλ κ²½μ°, μ΄ λ³κ²½μΌλ‘ μΈν΄ μ κ·Όμ΄ λΆκ°λ₯ν΄μ§ μ μμ΅λλ€/κ²½λ‘ μ μΈ μ κ±°λ‘ λ©μΈ νμ΄μ§ μ κ·Όμ μν₯μ μ€ μ μμ΅λλ€
λ€μ μ€ν¬λ¦½νΈλ₯Ό μ€ννμ¬ μν₯μ λ°λ μλν¬μΈνΈλ€μ νμΈν΄μ£ΌμΈμ:
π Script executed:
#!/bin/bash
# /api/auth/ κ²½λ‘λ₯Ό μ¬μ©νλ 컨νΈλ‘€λ¬ νμΈ
ast-grep --pattern $'@RequestMapping($$$)
class $CLASS {
$$$
}' | rg -A 10 -B 5 "api/auth"
# λ£¨νΈ κ²½λ‘ λ§€ν νμΈ
ast-grep --pattern $'@RequestMapping($$$)
$$$' | rg -A 5 -B 5 '^\s*"/"'Length of output: 164
λ€μ μ€ν¬λ¦½νΈλ‘ /api/auth λ° λ£¨νΈ κ²½λ‘ λ§€νμ΄ μ€μ λ‘ μ‘΄μ¬νλμ§ λ€μ νμΈν΄μ£ΌμΈμ:
π Script executed:
#!/bin/bash
# 1. /api/auth κ²½λ‘ μ¬μ© μ¬λΆ νμΈ
grep -R "/api/auth" -n SpringBoot/src/main/java
# 2. λ©μλ/ν΄λμ€ λ 벨μμ 루νΈ("/") λ§€ν νμΈ (@GetMapping, @PostMapping, @RequestMapping)
grep -R -E '@(GetMapping|PostMapping|RequestMapping).*"\/"' -n SpringBoot/src/main/java
# 3. path μμ± λ°©μμ λ£¨νΈ λ§€ν νμΈ (@RequestMapping(path = "/"))
grep -R 'path *= *"/"' -n SpringBoot/src/main/javaLength of output: 1015
/api/auth κ²½λ‘ μμΈ μ²λ¦¬ λλ½ νμΈ λ° μμ νμ
JwtAuthenticationFilter.javaμ shouldNotFilter λ©μλμ μΈμ¦ κ΄λ ¨ μλν¬μΈνΈμΈ /api/auth/*κ° μ μΈλμ§ μμ, λ‘κ·ΈμΈΒ·ν ν°λ°κΈΒ·λ‘κ·Έμμ API μ κ·Όμ΄ μ°¨λ¨λ©λλ€. 루νΈ(/) λ§€ν 컨νΈλ‘€λ¬λ λ°κ²¬λμ§ μμ λ³λ μν₯μ μμΌλ, μ μ 리μμ€λ SPA μ§μ
μ μ΄ μλ κ²½μ° μμΈ κ²ν κ° νμν©λλ€.
- μμΉ:
SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java (μ½ 142ν) - μμ μ μ:
/api/auth/κ²½λ‘λ₯Ό μ μΈ λͺ©λ‘μ μΆκ° (path.startsWith("/api/auth/"))
μμ diff:
return path.startsWith("/login/oauth2/")
|| path.startsWith("/oauth2/")
+ || path.startsWith("/api/auth/")
|| path.startsWith("/css/")
|| path.startsWith("/js/")
|| path.startsWith("/images/");νμ μ λ£¨νΈ κ²½λ‘ μμΈκ° νμνλ€λ©΄ || path.equals("/")λ ν¨κ» μΆκ°νμΈμ.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return path.startsWith("/login/oauth2/") | |
| || path.startsWith("/oauth2/") | |
| || path.equals("/") | |
| || path.startsWith("/css/") | |
| || path.startsWith("/js/") | |
| || path.startsWith("/images/"); | |
| return path.startsWith("/login/oauth2/") | |
| || path.startsWith("/oauth2/") | |
| || path.startsWith("/api/auth/") | |
| || path.startsWith("/css/") | |
| || path.startsWith("/js/") | |
| || path.startsWith("/images/"); |
π€ Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java
around lines 142 to 146, the shouldNotFilter method is missing exclusion for the
/api/auth/ path, causing authentication-related endpoints to be blocked. Add a
condition to exclude paths starting with "/api/auth/" by including
`path.startsWith("/api/auth/")` in the return statement. Optionally, if root
path exclusion is needed, also add `path.equals("/")`.
κΈ°μ‘΄ try ~ catch ꡬ쑰 μ κ±° μ μ νΈλ€λ¬μμ μ€λ₯μ²λ¦¬ μ μ μ Setting κ°μ 3κ°λ‘ μ ννλ λ‘μ§ μΆκ°
There was a problem hiding this comment.
Actionable comments posted: 2
β»οΈ Duplicate comments (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java (1)
61-83: Setting μν°ν° μ μ₯ λλ½ λ¬Έμ (μ€λ³΅ μ΄μ)μ€μ μ ν λ‘μ§ μΆκ°λ μ’μ κ°μ μ¬νμ λλ€. νμ§λ§ μ΄μ 리뷰μμ μ§μ λ Setting μν°ν° μ μ₯ λλ½ λ¬Έμ κ° μ¬μ ν μ‘΄μ¬ν©λλ€.
setting.getId()λ₯Ό λ°ννλ €λ©΄ λ¨Όμ μν°ν°κ° μ μ₯λμ΄μΌ ν©λλ€.λ€μκ³Ό κ°μ΄ μμ νμΈμ:
setting.setUser(user); + +settingRepository.save(setting); saveSettingKeyword(settingDTO, setting);
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (4)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/HotTopic_Description(1 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Controller/SettingController.java(3 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java(4 hunks)SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/GlobalExceptionHandler.java(2 hunks)
π§ Files skipped from review as they are similar to previous changes (2)
- SpringBoot/src/main/java/Baemin/News_Deliver/Domain/HotTopic/HotTopic_Description
- SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Controller/SettingController.java
π Additional comments (3)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/GlobalExceptionHandler.java (1)
46-54: μ½λ μ€νμΌ κ°μ μΉμΈSettingException νΈλ€λ¬μ μ½λ ν¬λ§·ν μ΄ κ°μ λμ΄ κ°λ μ±μ΄ ν₯μλμμ΅λλ€.
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java (2)
95-122: λ©μλ μκ·Έλμ² κ°μν μΉμΈResponseEntity λ°ν νμ μ voidλ‘ λ³κ²½νμ¬ λ©μλκ° κ°μνλμμ΅λλ€. κΆν μ²΄ν¬ λ‘μ§κ³Ό λ‘κΉ λ©μμ§λ μ μ ν μ μ§λκ³ κ°μ λμμ΅λλ€.
156-177: μννΈ μμ λ‘μ§ κ°μ μΉμΈλ©μλ μκ·Έλμ²κ° κ°μνλκ³ λ‘κΉ λ©μμ§κ° κ°μ λμμ΅λλ€. μννΈ μμ λ‘μ§μ΄ μ μ ν μ μ§λμ΄ λ°μ΄ν° 무결μ±μ΄ 보μ₯λ©λλ€.
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
|
||
| @ControllerAdvice | ||
| @RestControllerAdvice |
There was a problem hiding this comment.
π οΈ Refactor suggestion
μ€λ³΅ μ΄λ Έν μ΄μ μ¬μ© μ κ±° νμ
@ControllerAdviceμ @RestControllerAdviceλ₯Ό λμμ μ¬μ©νκ³ μμ΅λλ€. @RestControllerAdviceλ @ControllerAdvice + @ResponseBodyμ μ‘°ν©μ΄λ―λ‘ νλλ§ μ¬μ©νλ©΄ λ©λλ€.
λ€μκ³Ό κ°μ΄ μμ νμΈμ:
-@ControllerAdvice
-@RestControllerAdvice
+@RestControllerAdviceπ Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import org.springframework.web.bind.annotation.RestControllerAdvice; | |
| @ControllerAdvice | |
| @RestControllerAdvice | |
| import org.springframework.web.bind.annotation.RestControllerAdvice; | |
| @RestControllerAdvice | |
| public class GlobalExceptionHandler { | |
| // ... | |
| } |
π€ Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/GlobalExceptionHandler.java
around lines 12 to 15, remove the redundant @ControllerAdvice annotation and
keep only @RestControllerAdvice, since @RestControllerAdvice already includes
@ControllerAdvice and @ResponseBody functionality.
| /** | ||
| * μ¬μ©μμ νμ±μ€μΈ μ€μ μ΄ 3κ°κ° λλμ§ μλμ§ κ²μ¬νλ λ©μλ (λ΄λΆ λ©μλ) | ||
| * */ | ||
| private boolean isSettingLimitExceeded(User user) { | ||
| List<Setting> settingList = settingRepository.findActiveSettings(user, LocalDateTime.now()); | ||
|
|
||
| return settingList.size() > 3; | ||
| } |
There was a problem hiding this comment.
μ€μ μ ν λ‘μ§ μ€λ₯ μμ νμ
λ©μλλͺ
μ isSettingLimitExceededμ΄μ§λ§ μ€μ λ‘μ§μ > 3μΌλ‘ λμ΄ μμ΄ 4κ°κΉμ§ νμ©λ©λλ€. 3κ° μ νμ μλνλ€λ©΄ >= 3 쑰건μ μ¬μ©ν΄μΌ ν©λλ€.
λ€μκ³Ό κ°μ΄ μμ νμΈμ:
- return settingList.size() > 3;
+ return settingList.size() >= 3;λλ λ©μλ λ¬Έμνλ₯Ό λͺ νν νμΈμ:
/**
- * μ¬μ©μμ νμ±μ€μΈ μ€μ μ΄ 3κ°κ° λλμ§ μλμ§ κ²μ¬νλ λ©μλ (λ΄λΆ λ©μλ)
+ * μ¬μ©μμ νμ±μ€μΈ μ€μ μ΄ 3κ° μ΄μμΈμ§ κ²μ¬νλ λ©μλ (λ΄λΆ λ©μλ)
* */π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * μ¬μ©μμ νμ±μ€μΈ μ€μ μ΄ 3κ°κ° λλμ§ μλμ§ κ²μ¬νλ λ©μλ (λ΄λΆ λ©μλ) | |
| * */ | |
| private boolean isSettingLimitExceeded(User user) { | |
| List<Setting> settingList = settingRepository.findActiveSettings(user, LocalDateTime.now()); | |
| return settingList.size() > 3; | |
| } | |
| /** | |
| * μ¬μ©μμ νμ±μ€μΈ μ€μ μ΄ 3κ° μ΄μμΈμ§ κ²μ¬νλ λ©μλ (λ΄λΆ λ©μλ) | |
| * */ | |
| private boolean isSettingLimitExceeded(User user) { | |
| List<Setting> settingList = settingRepository.findActiveSettings(user, LocalDateTime.now()); | |
| return settingList.size() >= 3; | |
| } |
π€ Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java
around lines 221 to 228, the method isSettingLimitExceeded currently returns
true only if the active settings count is greater than 3, allowing 4 active
settings. To enforce a 3-setting limit as intended, change the condition to use
>= 3 instead of > 3. Alternatively, update the method documentation to clearly
state the current behavior if you want to keep the logic as is.
Grafanaλ‘ Prometheus λ°μ΄ν° μκ°ν
SpringBoot μλ²μ Redis μλ²μ λν λͺ¨λν°λ§ μΈν ꡬν
Redis-cache μλ²λ₯Ό νμ©νμ¬ λ‘λ μκ° κ°μ
API νΈμΆ μ DB μ μ ν λ°ν> 608ms
API νΈμΆ μ μΊμμμ λ°ν > 1ms
DB 쿼리 κ°μ λ° μΈλ±μ€ μΆκ°
μ€λ³΅ μ κ±° λ‘μ§ μν μ SQLμ λ³΅ν© μΈλ±μ€λ₯Ό μ μ©λ°μ μ μκ²λ 쿼리λ₯Ό λ³κ²½
μ²λ¦¬ μκ° κ°μ
Summary by CodeRabbit
μ κ· κΈ°λ₯
λ²κ·Έ μμ
μ±λ₯ κ°μ
μ€μ
κΈ°ν