Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ dependencies {

// Discord Webhook
implementation("club.minnced:discord-webhooks:0.8.4")

// awaitility
testImplementation 'org.awaitility:awaitility:4.3.0'
}

// ------------------
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/pitchain/bmscrap/domain/BmScrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "bm_id"}))
public class BmScrap {

@Id
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/com/pitchain/common/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.pitchain.common.config;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

@Bean
public Executor asyncTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//TODO - corePoolSize, maxPoolSize, queueCapacity는 서비스에 맞게 테스트를 통해 설정
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
// executor.setQueueCapacity();
Comment on lines +24 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

스레드 풀 설정을 재검토해주세요.

현재 설정에 몇 가지 개선이 필요합니다:

  1. corePoolSizemaxPoolSize를 동일하게 설정하면 스레드 풀의 탄력성이 제한됩니다
  2. Redis 조회수 업데이트와 같은 I/O 집약적 작업의 경우, CPU 코어 수보다 더 많은 스레드가 필요할 수 있습니다
  3. queueCapacity 설정이 없으면 기본 무제한 큐가 사용되어 메모리 문제가 발생할 수 있습니다

다음과 같은 설정을 고려해보세요:

-        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
-        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
-//        executor.setQueueCapacity();
+        int processors = Runtime.getRuntime().availableProcessors();
+        executor.setCorePoolSize(processors);
+        executor.setMaxPoolSize(processors * 2); // I/O 집약적 작업 고려
+        executor.setQueueCapacity(100); // 적절한 큐 용량 설정
📝 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.

Suggested change
//TODO - corePoolSize, maxPoolSize, queueCapacity는 서비스에 맞게 테스트를 통해 설정
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
// executor.setQueueCapacity();
//TODO - corePoolSize, maxPoolSize, queueCapacity는 서비스에 맞게 테스트를 통해 설정
int processors = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(processors);
executor.setMaxPoolSize(processors * 2); // I/O 집약적 작업 고려
executor.setQueueCapacity(100); // 적절한 큐 용량 설정
🤖 Prompt for AI Agents
In src/main/java/com/pitchain/common/config/AsyncConfig.java around lines 24 to
27, the thread pool configuration needs improvement. Adjust corePoolSize to a
value less than maxPoolSize to allow elasticity, increase maxPoolSize beyond the
number of CPU cores to better handle I/O intensive tasks like Redis updates, and
set a finite queueCapacity to prevent potential memory issues from an unbounded
queue. Update the executor configuration accordingly to reflect these changes.

executor.setTaskDecorator(new MdcTaskDecorator());
executor.setThreadNamePrefix("async");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();

return executor;
}
Comment on lines +21 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Redis 조회수 업데이트 용도로 스레드 풀 설정을 검토해주세요.

현재 설정은 SpViewsService@Async 메서드를 지원하기 위한 것으로 보입니다. Redis I/O 작업의 특성을 고려하면:

  1. 처리량 최적화: Redis 호출은 I/O 대기가 발생하므로 CPU 코어 수보다 많은 스레드가 도움이 될 수 있습니다
  2. 메모리 관리: 큐 용량 제한으로 과도한 메모리 사용을 방지해야 합니다
  3. 성능 테스트: TODO 주석에서 언급한 대로 실제 부하 테스트를 통한 최적화가 필요합니다

다음 스크립트로 현재 설정이 Redis 작업에 적합한지 확인해보세요:


🏁 Script executed:

#!/bin/bash
# Redis 연결 및 응답 시간 테스트를 위한 설정 확인

# SpViewsService에서 사용하는 Redis 작업 패턴 확인
rg -A 10 -B 2 "redisHashRepository\.increment"

# 현재 시스템의 프로세서 수 확인
echo "Available processors: $(nproc)"

# 동시 Redis 연결 테스트 스크립트 생성 제안
echo "동시성 테스트를 위해 Redis 연결 풀과 스레드 풀 설정을 비교 검토하세요."

Length of output: 2822


Redis I/O 특화 스레드 풀 설정 개선 필요

AsyncConfig.java(21–34)에서 현재 CPU 코어 수만큼 스레드를 생성하도록 되어 있는데, Redis 호출은 I/O 대기 시간이 크므로 아래와 같이 수정하세요.

  • corePoolSize, maxPoolSize를 하드코딩 대신 프로퍼티로 외부화
  • queueCapacity를 명시적으로 설정해 과도한 요청 폭주 방지
  • application.yml에 기본값을 두고, 운영 환경 부하 테스트를 통해 최적값 확인

예시 변경안:

 src/main/java/com/pitchain/common/config/AsyncConfig.java

     @Bean
     public Executor asyncTaskExecutor(
-        /* 하드코딩된 값 */
-        ) {
-        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
-        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
-        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
-        // executor.setQueueCapacity();
+        @Value("${async.core-pool-size}") int corePoolSize,
+        @Value("${async.max-pool-size}")  int maxPoolSize,
+        @Value("${async.queue-capacity}") int queueCapacity
+    ) {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(corePoolSize);
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setQueueCapacity(queueCapacity);
         executor.setTaskDecorator(new MdcTaskDecorator());
         executor.setThreadNamePrefix("async");
         executor.setWaitForTasksToCompleteOnShutdown(true);
         executor.initialize();

         return executor;
     }

application.yml 예시:

async:
  core-pool-size: 8
  max-pool-size: 16
  queue-capacity: 500

• 위 설정으로 Redis I/O 처리량과 메모리 사용을 균형 있게 관리하세요.
• 실제 부하(특히 Redis 업데이트 빈도)에 맞춰 부하 테스트 후 프로퍼티 값을 조정해 주세요.

🤖 Prompt for AI Agents
In src/main/java/com/pitchain/common/config/AsyncConfig.java lines 21 to 34, the
thread pool configuration uses hardcoded CPU core counts which is suboptimal for
Redis I/O operations. Modify the code to externalize corePoolSize, maxPoolSize,
and queueCapacity as configurable properties loaded from application.yml. Set
explicit queueCapacity to prevent request overload. Add corresponding properties
in application.yml with default values (e.g., core-pool-size: 8, max-pool-size:
16, queue-capacity: 500) and update the AsyncConfig class to read these values
via @Value or @ConfigurationProperties annotations. This allows tuning thread
pool sizes based on load testing results.


@Override
public Executor getAsyncExecutor() {
return asyncTaskExecutor();
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}

@Slf4j
private static class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
log.error("비동기 실행 중 에러가 발생했습니다: ", throwable);
}
}

@Slf4j
private static class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
}

}
16 changes: 16 additions & 0 deletions src/main/java/com/pitchain/common/config/RedisConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.support.ResourceScriptSource;

import java.io.IOException;
import java.util.List;

@Configuration
@EnableRedisRepositories
Expand Down Expand Up @@ -45,4 +53,12 @@ public MessageListenerAdapter messageListenerAdapter(RedisSubscriber subscriber)
return new MessageListenerAdapter(subscriber, "onMessage");
}

@Bean
public RedisScript<List> script() throws IOException {
ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("META-INF/scripts/getanddeleteall.lua"));
String script = scriptSource.getScriptAsString();

return new DefaultRedisScript<>(script, List.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
import com.pitchain.common.apiPayload.ErrorStatus;
import jakarta.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {
Expand Down Expand Up @@ -72,4 +75,17 @@ public ResponseEntity<CustomResponse> handleJWTVerificationException(JWTVerifica
.status(HttpStatus.UNAUTHORIZED)
.body(customResponse);
}

@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<CustomResponse> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
log.error("DataIntegrityViolationException = {}", e.getMessage());
String errorMessage = "DataIntegrityViolationException(제약 조건 위반 오류)가 발생했습니다.";

HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
CustomResponse customResponse = CustomResponse.onFailure(httpStatus.name(), errorMessage);

return ResponseEntity
.status(httpStatus)
.body(customResponse);
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/pitchain/common/redis/RedisHashRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.pitchain.common.redis;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Map;

@Component
@RequiredArgsConstructor
public class RedisHashRepository {

private final RedisTemplate<String, String> redisTemplate;
private final RedisScript<List> script;

public void increment(String key, String hashKey, Long value) {
redisTemplate.opsForHash().increment(key, hashKey, value);
}

public Map<String, String> findAll(String key) {
HashOperations<String, String, String> ops = redisTemplate.opsForHash();
return ops.entries(key);
}

public List<String> getAndDeleteAll(String key) {
return redisTemplate.execute(script, Collections.singletonList(key));
}

}
6 changes: 4 additions & 2 deletions src/main/java/com/pitchain/sp/application/SpService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
public class SpService {
private final SpCommandService spCommandService;
private final SpQueryService spQueryService;
private final SpViewsService spViewsService;

@Transactional
public void createSp(MemberDetails memberDetails, Long bmId, SpCreateReq spCreateReq, MultipartFile thumbnailImg) {
Expand All @@ -34,9 +35,10 @@ public InfinityScrollRes<SpDetailRes> getSpDetailsFilteredCategory(MemberDetails
return spQueryService.getSpDetailsFilteredCategory(memberDetails, mainCategoryInKorean, lastSpId, size);
}

@Transactional(readOnly = true)
public SpDetailRes getSpDetail(MemberDetails memberDetails, Long bmId, Long spId) {
return spQueryService.getSpDetail(memberDetails, bmId, spId);
SpDetailRes spDetailRes = spQueryService.getSpDetail(memberDetails, bmId, spId);
spViewsService.updateSpView(spId);
return spDetailRes;
}

@Transactional
Expand Down
82 changes: 82 additions & 0 deletions src/main/java/com/pitchain/sp/application/SpViewsService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.pitchain.sp.application;

import com.pitchain.common.redis.RedisHashRepository;
import com.pitchain.sp.infrastucture.SpRepositoryCustom;
import com.pitchain.sp.infrastucture.dto.SpViewsDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SpViewsService {

private final SpRepositoryCustom spRepositoryCustom;
private final RedisHashRepository redisHashRepository;

private static final String SP_VIEW_REDIS_KEY = "spView";

/**
* Redis에 Sp 조회수 증가
* @param spId
*/
@Async
public void updateSpView(Long spId) {
redisHashRepository.increment(SP_VIEW_REDIS_KEY, String.valueOf(spId), 1L);
}

/**
* Redis에서 DB로 Sp 조회수 업데이트
*/
@Transactional
public void updateSpViews() {
List<String> spViewsResult = redisHashRepository.getAndDeleteAll(SP_VIEW_REDIS_KEY);
List<SpViewsDto> spViewsDtoList = parseResult(spViewsResult);

for (SpViewsDto spViewsDto : spViewsDtoList) {
spRepositoryCustom.updateSpView(spViewsDto.spId(), spViewsDto.views());
}
}

/**
* 1분마다 Redis에서 DB로 Sp 조회수 업데이트하는 작업 수행
*/
@Scheduled(cron = "0 */1 * * * *")
public void runUpdateSpViews() {
try {
updateSpViews();
} catch (Exception e) {
log.error("Scheduling task [runUpdateSpViews] failed", e);
}
}

/**
* Redis에서 가져온 Sp 조회수 String 리스트를 Dto 리스트로 파싱
* @param List<String>
* @return List<SpViewsDto>
*/
private List<SpViewsDto> parseResult(List<String> spViewsStringList) {
if (spViewsStringList.size() % 2 != 0){
log.error("spViewsStringList 개수가 올바르지 않습니다.");
throw new IllegalArgumentException("spViewsStringList 개수가 올바르지 않습니다.");
}

List<SpViewsDto> spViewsDtoList = new ArrayList<>();
for (int i = 0; i < spViewsStringList.size(); i += 2) {
Long spId = Long.parseLong(spViewsStringList.get(i));
Long views = Long.parseLong(spViewsStringList.get(i + 1));

SpViewsDto spViewsDto = new SpViewsDto(spId, views);
spViewsDtoList.add(spViewsDto);
}

return spViewsDtoList;
}
Comment on lines +65 to +81
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

파싱 로직에 예외 처리 개선 필요

기본적인 검증은 있지만 Long.parseLong() 실행 시 발생할 수 있는 NumberFormatException에 대한 처리가 필요합니다.

 private List<SpViewsDto> parseResult(List<String> spViewsStringList) {
     if (spViewsStringList.size() % 2 != 0){
         log.error("spViewsStringList 개수가 올바르지 않습니다.");
         throw new IllegalArgumentException("spViewsStringList 개수가 올바르지 않습니다.");
     }

     List<SpViewsDto> spViewsDtoList = new ArrayList<>();
     for (int i = 0; i < spViewsStringList.size(); i += 2) {
-        Long spId = Long.parseLong(spViewsStringList.get(i));
-        Long views = Long.parseLong(spViewsStringList.get(i + 1));
+        try {
+            Long spId = Long.parseLong(spViewsStringList.get(i));
+            Long views = Long.parseLong(spViewsStringList.get(i + 1));
+            
+            SpViewsDto spViewsDto = new SpViewsDto(spId, views);
+            spViewsDtoList.add(spViewsDto);
+        } catch (NumberFormatException e) {
+            log.error("Invalid number format in Redis data: spId={}, views={}", 
+                     spViewsStringList.get(i), spViewsStringList.get(i + 1), e);
+            // 잘못된 데이터는 건너뛰고 계속 처리
+        }

-        SpViewsDto spViewsDto = new SpViewsDto(spId, views);
-        spViewsDtoList.add(spViewsDto);
     }

     return spViewsDtoList;
 }
📝 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.

Suggested change
private List<SpViewsDto> parseResult(List<String> spViewsStringList) {
if (spViewsStringList.size() % 2 != 0){
log.error("spViewsStringList 개수가 올바르지 않습니다.");
throw new IllegalArgumentException("spViewsStringList 개수가 올바르지 않습니다.");
}
List<SpViewsDto> spViewsDtoList = new ArrayList<>();
for (int i = 0; i < spViewsStringList.size(); i += 2) {
Long spId = Long.parseLong(spViewsStringList.get(i));
Long views = Long.parseLong(spViewsStringList.get(i + 1));
SpViewsDto spViewsDto = new SpViewsDto(spId, views);
spViewsDtoList.add(spViewsDto);
}
return spViewsDtoList;
}
private List<SpViewsDto> parseResult(List<String> spViewsStringList) {
if (spViewsStringList.size() % 2 != 0){
log.error("spViewsStringList 개수가 올바르지 않습니다.");
throw new IllegalArgumentException("spViewsStringList 개수가 올바르지 않습니다.");
}
List<SpViewsDto> spViewsDtoList = new ArrayList<>();
for (int i = 0; i < spViewsStringList.size(); i += 2) {
try {
Long spId = Long.parseLong(spViewsStringList.get(i));
Long views = Long.parseLong(spViewsStringList.get(i + 1));
SpViewsDto spViewsDto = new SpViewsDto(spId, views);
spViewsDtoList.add(spViewsDto);
} catch (NumberFormatException e) {
log.error(
"Invalid number format in Redis data: spId={}, views={}",
spViewsStringList.get(i),
spViewsStringList.get(i + 1),
e
);
// 잘못된 데이터는 건너뛰고 계속 처리
}
}
return spViewsDtoList;
}
🤖 Prompt for AI Agents
In src/main/java/com/pitchain/sp/application/SpViewsService.java around lines 63
to 79, the parseResult method lacks handling for NumberFormatException that can
be thrown by Long.parseLong. Add try-catch blocks around the parsing statements
to catch NumberFormatException, log an appropriate error message, and throw a
meaningful exception to handle invalid number formats gracefully.

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public record SpDetailRes(
@S3Url
String thumbnailImgURL,
@NotNull
Integer views,
Long views,
@NotBlank
String name,
@NotBlank
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/pitchain/sp/domain/Sp.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class Sp extends BaseEntity {
@Column(nullable = false)
private String thumbnailImgKey;

private int views = 0;
private Long views;

@Column(nullable = false)
private String name;
Expand All @@ -42,7 +42,7 @@ public static Sp of(Bm bm, String thumbnailImgKey, String name) {
sp.thumbnailImgKey = thumbnailImgKey;
sp.name = name;
sp.spStatus = SpStatus.TRANSCODING;
sp.views = 0;
sp.views = 0L;
return sp;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

Expand Down Expand Up @@ -46,4 +47,13 @@ private static BooleanExpression eqMainCategory(MainCategory category) {
private BooleanExpression ltSpId(Long lastSpId) {
return lastSpId == null ? null : sp.id.lt(lastSpId);
}

@Transactional
public long updateSpView(Long spId, Long views) {
return queryFactory
.update(sp)
.set(sp.views, sp.views.add(views))
.where(sp.id.eq(spId))
.execute();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.pitchain.sp.infrastucture.dto;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

패키지명 오타 확인 요청

패키지명에 "infrastucture"가 "infrastructure"의 오타로 보입니다. 프로젝트 전체에서 일관된 명명 규칙을 사용하고 있는지 확인해 주세요.


🏁 Script executed:

#!/bin/bash
# 프로젝트 내 infrastructure/infrastucture 패키지명 사용 현황 확인
fd -t d "infrastucture"
fd -t d "infrastructure"

Length of output: 771


패키지명 철자 통일 필요

프로젝트에 com.pitchain.* 아래에 "infrastucture"(오타)와 "infrastructure"(올바름)가 혼용되어 있어, 모든 패키지를 "infrastructure"로 일관되게 리팩토링해야 합니다. IDE 리팩터링 기능 또는 스크립트를 활용해 디렉토리명과 package 선언, import 구문을 일괄 수정해 주세요.

영향 받는 오타 패키지 경로:

  • src/main/java/com/pitchain/bm/infrastucture/
  • src/main/java/com/pitchain/bmscrap/infrastucture/
  • src/main/java/com/pitchain/comment/infrastucture/
  • src/main/java/com/pitchain/investment/infrastucture/
  • src/main/java/com/pitchain/member/infrastucture/
  • src/main/java/com/pitchain/mysphistory/infrastucture/
  • src/main/java/com/pitchain/notificationhistory/infrastucture/
  • src/main/java/com/pitchain/sp/infrastucture/
  • src/main/java/com/pitchain/splike/infrastucture/
🤖 Prompt for AI Agents
In src/main/java/com/pitchain/sp/infrastucture/dto/SpViewsDto.java at line 1,
the package name "infrastucture" is misspelled and should be corrected to
"infrastructure". Refactor all occurrences of the misspelled package name in the
project, including directory names, package declarations, and import statements,
to use the correct spelling "infrastructure". Use your IDE's refactoring tools
or scripts to perform this change consistently across all affected packages
listed.


public record SpViewsDto(Long spId, Long views) {
}
1 change: 1 addition & 0 deletions src/main/java/com/pitchain/splike/domain/SpLike.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "sp_id"}))
public class SpLike {

@Id
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/META-INF/scripts/getanddeleteall.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local data = redis.call('HGETALL', KEYS[1])
redis.call('DEL', KEYS[1])
return data
Loading