-
Notifications
You must be signed in to change notification settings - Fork 0
[REFACTOR] 답변 랭킹 저장이 답변 저장에 영향을 주지 않도록 분리 #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough답변 랭킹 처리를 트랜잭션 종료 후 비동기 이벤트 기반으로 분리하고, 빈(Blank) 답변 무시 로직을 추가했다. AnswerRankingEntity의 content 컬럼을 TEXT로 변경했다. PR 분석 관련 불필요 타입 삭제와 Redis 퍼블리싱 직렬화 제거를 포함하며, 관련 테스트가 추가되었다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Controller
participant QuestionFacadeService as Service
participant Tx as DB Tx
participant SpringEvents as AppEventPublisher
participant AnswerEventListener as Listener
participant AnswerRankingService as Ranking
User->>Controller: Update Answer request
Controller->>Service: updateAnswer(answerId, content, userId)
activate Service
Service->>Tx: save/update Answer (within @Transactional)
Service->>SpringEvents: publish UpdateAnswerEvent(answer, userId)
deactivate Service
note over Tx: Commit
SpringEvents-->>AnswerEventListener: UpdateAnswerEvent (AFTER_COMMIT, async)
activate AnswerEventListener
AnswerEventListener->>Ranking: push(answer, userId)
deactivate AnswerEventListener
opt Blank content
note over Ranking: if answer.isBlank() return
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit ad72ca2. |
📝 Test Coverage Report
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (10)
gss-domain/src/main/java/com/devoops/domain/entity/github/answer/Answer.java (2)
21-23: Micrometer StringUtils 대신 표준 API로 대체하세요.도메인 계층에서
io.micrometer.common.util.StringUtils의존은 과합니다.String#isBlank()로 충분하며,null가능성까지 고려하려면content == null || content.isBlank()로 처리하면 됩니다.적용 diff:
- public boolean isBlank() { - return StringUtils.isBlank(content); - } + public boolean isBlank() { + return content == null || content.isBlank(); + }
3-3: 불필요한 유틸 import 제거.적용 diff:
-import io.micrometer.common.util.StringUtils;gss-api-app/src/test/java/com/devoops/service/answerranking/AnswerRankingServiceTest.java (2)
21-23: 미사용 imports 정리 또는 파라미터라이즈드 테스트로 전환.
@ParameterizedTest,@NullAndEmptySource를 사용하지 않습니다. 제거하거나, 아래 제안처럼 공백 케이스까지 커버하는 파라미터 테스트로 바꿔주세요.불필요 import 제거 diff:
-import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource;(선택) 공백/탭까지 커버하는 파라미터 테스트 예시:
@ParameterizedTest @NullAndEmptySource @ValueSource(strings = {" ", "\t", "\n"}) void 비어있거나_공백이면_랭킹에_반영하지_않는다(String body) { ... }
52-65: 빈 답변 미반영 테스트 추가는 적절합니다.시나리오가 명확하고, 랭킹이 비어 있는지 검증이 정확합니다. 공백 전용 케이스(예: " ", "\t")도 추가되면 더 견고해집니다.
gss-api-app/src/test/java/com/devoops/service/facade/QuestionFacadeServiceTest.java (2)
121-147: 단일 이벤트 단정식을 더 간결하게 리팩터링하세요.동일 스트림에 대해
allMatch를 두 번 사용하는 대신 단일 요소 단정으로 의도를 명확히 할 수 있습니다.- assertThat(applicationEvents.stream(UpdateAnswerEvent.class)) - .hasSize(1) - .allMatch(event -> event.getAnswer().getId().equals(answer1.getId())) - .allMatch(event -> event.getUserId() == user.getId()); + assertThat(applicationEvents.stream(UpdateAnswerEvent.class)) + .singleElement() + .satisfies(event -> { + assertThat(event.getAnswer().getId()).isEqualTo(answer1.getId()); + assertThat(event.getUserId()).isEqualTo(user.getId()); + });
133-147: AFTER_COMMIT 리스너 동작 보장 여부를 별도 통합 테스트로 검증 필요.본 테스트는 “발행”만 확인합니다.
@TransactionalEventListener(phase = AFTER_COMMIT)와@Async가 실제 커밋 이후에 호출되는지(롤백 시 미호출 포함) SpyBean/CountDownLatch 등을 이용해 통합 테스트로 한 번 더 검증해 주세요.원하시면 통합 테스트 스캐폴드를 드리겠습니다.
gss-api-app/src/main/java/com/devoops/event/listener/AnswerEventListener.java (2)
18-24: 비동기 예외 로깅/관측 가능성을 확보하세요.
@Async에서는 예외가 호출자에게 전파되지 않습니다. 최소한 로깅을 추가해 실패를 관측 가능하게 하거나, 커스텀AsyncUncaughtExceptionHandler를 등록하세요.-import lombok.RequiredArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @@ -@RequiredArgsConstructor +@RequiredArgsConstructor +@Slf4j public class AnswerEventListener { @@ - answerRankingService.push(answer, userId); + try { + answerRankingService.push(answer, userId); + } catch (Exception e) { + log.error("Failed to push answer ranking. answerId={}, userId={}", answer.getId(), userId, e); + }
21-23: 이벤트 페이로드를 “엔티티” 대신 식별자 위주로 경량화 고려.지연 로딩/분리(detached) 엔티티 접근 리스크를 줄이기 위해
answerId만 전달하고, 리스너에서 조회해 사용하는 방식을 검토해 주세요. 성능/일관성 요구사항에 따라 선택입니다.gss-api-app/src/main/java/com/devoops/event/UpdateAnswerEvent.java (1)
7-18: 현대식 이벤트 스타일로 단순화 가능합니다.Spring 4.2+에서는
ApplicationEvent상속이 필수 아닙니다. 단순 POJO/record 이벤트로 전환하면 보일러플레이트가 줄고 테스트도 용이합니다.-package com.devoops.event; - -import com.devoops.domain.entity.github.answer.Answer; -import lombok.Getter; -import org.springframework.context.ApplicationEvent; - -@Getter -public class UpdateAnswerEvent extends ApplicationEvent { - - private final Answer answer; - private final long userId; - - public UpdateAnswerEvent(Object source, Answer answer, long userId) { - super(source); - this.answer = answer; - this.userId = userId; - } -} +package com.devoops.event; + +import com.devoops.domain.entity.github.answer.Answer; + +public record UpdateAnswerEvent(Answer answer, long userId) {}퍼블리셔 호출부도 다음처럼 간단해집니다(참고):
eventPublisher.publishEvent(new UpdateAnswerEvent(answer, userId));gss-api-app/src/main/java/com/devoops/service/facade/QuestionFacadeService.java (1)
45-52: 대량 업데이트 시 랭킹 이벤트 미발행 — 의도인지 확인 필요.
updateAllAnswers에서는 이벤트를 발행하지 않아 랭킹 갱신이 누락됩니다. 의도된 동작인지 확인 부탁드립니다. 만약 갱신이 필요하다면:
AnswerPutRequest에userId를 포함하거나,- 현재 사용자 컨텍스트(SecurityContext 등)에서
userId를 취득하여, 결과Answers를 순회하며 이벤트를 발행하세요.원하시면 두 방식에 대한 구체 패치 제안을 드리겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
gss-api-app/src/main/java/com/devoops/event/AnalyzePrEvent.java(0 hunks)gss-api-app/src/main/java/com/devoops/event/UpdateAnswerEvent.java(1 hunks)gss-api-app/src/main/java/com/devoops/event/listener/AnswerEventListener.java(1 hunks)gss-api-app/src/main/java/com/devoops/event/publisher/PrAnalysisPublisher.java(1 hunks)gss-api-app/src/main/java/com/devoops/service/facade/QuestionFacadeService.java(3 hunks)gss-api-app/src/test/java/com/devoops/service/answerranking/AnswerRankingServiceTest.java(2 hunks)gss-api-app/src/test/java/com/devoops/service/facade/QuestionFacadeServiceTest.java(2 hunks)gss-client/gss-mcp-client/src/main/resources/application-mcp-client.yml(1 hunks)gss-domain/src/main/java/com/devoops/domain/entity/github/answer/Answer.java(2 hunks)gss-domain/src/main/java/com/devoops/domain/repository/github/answer/AnswerRankingDomainRepository.java(0 hunks)gss-domain/src/main/java/com/devoops/jpa/entity/github/answer/AnswerRankingEntity.java(1 hunks)gss-domain/src/main/java/com/devoops/jpa/repository/github/answer/AnswerRankingDomainRepositoryImpl.java(0 hunks)gss-domain/src/main/java/com/devoops/service/answerranking/AnswerRankingService.java(1 hunks)
💤 Files with no reviewable changes (3)
- gss-domain/src/main/java/com/devoops/domain/repository/github/answer/AnswerRankingDomainRepository.java
- gss-domain/src/main/java/com/devoops/jpa/repository/github/answer/AnswerRankingDomainRepositoryImpl.java
- gss-api-app/src/main/java/com/devoops/event/AnalyzePrEvent.java
🧰 Additional context used
🧬 Code graph analysis (1)
gss-api-app/src/main/java/com/devoops/service/facade/QuestionFacadeService.java (3)
gss-domain/src/main/java/com/devoops/service/answerranking/AnswerRankingService.java (1)
Service(13-46)gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)
Service(25-93)gss-api-app/src/main/java/com/devoops/service/facade/PullRequestFacadeService.java (1)
Service(17-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-push
🔇 Additional comments (4)
gss-api-app/src/main/java/com/devoops/event/publisher/PrAnalysisPublisher.java (1)
19-19: RedisTemplate value serializer 설정 검증 필요
gss-domain/src/main/java/com/devoops/config/RedisConfig.java에서GenericJackson2JsonRedisSerializer가 value serializer로 설정된 것이 확인되었습니다; 구독자(리스너) 측에서도 동일 설정인지 반드시 검증하세요.- 타입 불일치로 인한 역직렬화 예외 방지를 위해 계약 테스트 추가 또는 JSON 문자열 회귀 옵션 준비를 권장합니다.
gss-domain/src/main/java/com/devoops/service/answerranking/AnswerRankingService.java (1)
25-27: 로깅 및 트랜잭션 경계 추가 검토
answer.isBlank()블럭에 호출 스킵 로그 남기도록
log.debug("Skip ranking push: blank answer. userId={}, questionId={}", userId, answer.getQuestionId());- 내부 삭제→저장 2단계 쓰기를 하나의 트랜잭션으로 묶기 위해 클래스 또는
push()에@Transactional추가 검토- 이벤트 리스너가 기본 phase(AFTER_COMMIT)로 동작하는지 직접 확인
예시:
@Service @RequiredArgsConstructor @Slf4j +@Transactional public class AnswerRankingService { public void push(Answer answer, long userId) { if (answer.isBlank()) { - return; + log.debug("Skip ranking push: blank answer. userId={}, questionId={}", userId, answer.getQuestionId()); + return; } // …gss-api-app/src/test/java/com/devoops/service/facade/QuestionFacadeServiceTest.java (1)
17-17: 이벤트 캡처 설정 적절합니다.
UpdateAnswerEvent와RecordApplicationEvents/ApplicationEvents를 통한 캡처 구성이 테스트 목적에 부합합니다.Also applies to: 20-20, 24-26
gss-api-app/src/main/java/com/devoops/service/facade/QuestionFacadeService.java (1)
38-43: 트랜잭션 내 업데이트 후 이벤트 발행 흐름 적절합니다.
@Transactional+AFTER_COMMIT조합으로 랭킹 저장이 본 저장을 방해하지 않도록 잘 분리됐습니다.
| @Async | ||
| @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
| public void updateAnswer(UpdateAnswerEvent updateAnswerEvent) { | ||
| Answer answer = updateAnswerEvent.getAnswer(); | ||
| long userId = updateAnswerEvent.getUserId(); | ||
| answerRankingService.push(answer, userId); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
리스너에 트랜잭션 경계를 추가하세요 (특히 @async 환경).
커밋 후 다른 스레드에서 실행되므로, JPA/MyBatis를 사용한다면 쓰기 작업에 트랜잭션이 없어 실패(예: TransactionRequiredException)할 수 있습니다. 리스너 메서드에 REQUIRES_NEW 트랜잭션을 부여해 랭킹 저장을 독립적으로 보장하세요.
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
@@
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateAnswer(UpdateAnswerEvent updateAnswerEvent) {
Answer answer = updateAnswerEvent.getAnswer();
long userId = updateAnswerEvent.getUserId();
answerRankingService.push(answer, userId);
}대안: AnswerRankingService.push(...) 자체를 @Transactional로 감싸도 됩니다(서비스 계층 표준화).
📝 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.
| @Async | |
| @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | |
| public void updateAnswer(UpdateAnswerEvent updateAnswerEvent) { | |
| Answer answer = updateAnswerEvent.getAnswer(); | |
| long userId = updateAnswerEvent.getUserId(); | |
| answerRankingService.push(answer, userId); | |
| } | |
| import org.springframework.scheduling.annotation.Async; | |
| import org.springframework.stereotype.Component; | |
| import org.springframework.transaction.annotation.Propagation; | |
| import org.springframework.transaction.annotation.Transactional; | |
| import org.springframework.transaction.event.TransactionPhase; | |
| import org.springframework.transaction.event.TransactionalEventListener; | |
| @Component | |
| public class AnswerEventListener { | |
| @Async | |
| @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | |
| @Transactional(propagation = Propagation.REQUIRES_NEW) | |
| public void updateAnswer(UpdateAnswerEvent updateAnswerEvent) { | |
| Answer answer = updateAnswerEvent.getAnswer(); | |
| long userId = updateAnswerEvent.getUserId(); | |
| answerRankingService.push(answer, userId); | |
| } | |
| } |
🤖 Prompt for AI Agents
In gss-api-app/src/main/java/com/devoops/event/listener/AnswerEventListener.java
around lines 18 to 24, the @Async listener runs after commit on another thread
and currently has no transactional boundary, which can cause
TransactionRequiredException when performing writes; add a new transaction
boundary (e.g., annotate the listener method with a propagation of REQUIRES_NEW)
so the ranking save runs in its own transaction, or alternatively make
AnswerRankingService.push(...) @Transactional to ensure the write executes
within a transaction.
| - "summaryDetail"은 변경 내용을 항목별로 요약한 제목(title) + 설명(description) 쌍으로 구성해. | ||
| - "category"는 기술적인 관점에서 PR 코드 변경 내용을 반영하여 선택해 (예: 성능, 보안, 확장성, 유지보수성, 테스트 등) | ||
| - "question"은 각 category에 대해 기술 면접에서 사용할 수 있는 질문이어야 해. | ||
| - "question"은 가능한 255자가 넘지 않도록 짧고 명료하게 질문해. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
프롬프트 지시만으로는 255자 제한을 보장하지 못합니다 — 스키마/후처리로 강제하세요
LLM 프롬프트에만 의존하면 간헐적으로 255자를 초과할 수 있어 동일 계열의 제약 위반이 재발할 수 있습니다. JSON 스키마에 maxLength=255를 명시하고(format-message가 주입하는 스키마에 반영), 파서 단계에서 초과 문항은 안전하게 잘라내는 후처리를 추가하는 것을 권장합니다. 라인 문구도 “가능한”이 아닌 “반드시/초과 시 절단”으로 바꿔 의도를 강화해 주세요.
Apply this diff to tighten the instruction:
- - "question"은 가능한 255자가 넘지 않도록 짧고 명료하게 질문해.
+ - "question"은 반드시 255자 이내로 작성하고, 초과 시 앞 252자만 남기고 '...'로 절단해.다음 스크립트로 레포 내 강제 수단이 있는지 확인해 주세요(스키마 maxLength, DTO @SiZe, DB 컬럼 등):
필요하시면: (1) 스키마에 maxLength 추가, (2) 파서 후처리 유틸(초과 시 안전 절단), (3) 경계 테스트(254/255/256자) 작성까지 패치 드리겠습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== JSON 스키마에 maxLength 존재 여부 체크 =="
rg -n -C2 -i --type=json --type=yaml 'question"|questions"|prompt.*question|maxLength' || true
echo "== DTO/검증 애노테이션(@Size<=255) 존재 여부 체크 =="
rg -n -C2 -i --type=java --type=kt '@Size\s*\(\s*max\s*=\s*255' || true
rg -n -C2 -i --type=java --type=kt '\bString\s+question\b|\bvar\s+question\b|\bval\s+question\b' || true
echo "== DB 제약(컬럼 길이) 존재 여부 체크 =="
rg -n -C2 -i --type=sql 'question.*varchar\s*\(\s*255\s*\)|varchar\s*\(\s*255\s*\).*question' || trueLength of output: 2225
프롬프트 문구 및 제약 강제 적용
- gss-client/gss-mcp-client/src/main/resources/application-mcp-client.yml:11을
- - "question"은 가능한 255자가 넘지 않도록 짧고 명료하게 질문해. + - "question"은 반드시 255자 이내로 작성하고, 초과 시 앞 252자만 남기고 '...'로 절단해.
- 프로젝트 전반 JSON 스키마(application-mcp-client.yml 등)에 question 필드에
maxLength: 255명시 - 응답 파서 단계에 255자 초과 시 앞 252자만 남기고 ‘...’로 절단하는 후처리 로직 추가
📝 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.
| - "question"은 가능한 255자가 넘지 않도록 짧고 명료하게 질문해. | |
| - "question"은 반드시 255자 이내로 작성하고, 초과 시 앞 252자만 남기고 '...'로 절단해. |
🤖 Prompt for AI Agents
gss-client/gss-mcp-client/src/main/resources/application-mcp-client.yml around
line 11: the project requires enforcing the question length constraint and
truncation behavior; add "maxLength: 255" to the question field in the JSON
schema(s) (application-mcp-client.yml and any other schema files used across the
project) so validators reject >255 characters, and implement a response-parser
post-processing step that, when the parsed question exceeds 255 characters,
truncates it to the first 252 characters and appends "..." before
returning/storing the value.
| @Column(name = "content", columnDefinition = "TEXT") | ||
| private String questionContent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
DB 컬럼을 TEXT로 고정하면 벤더 종속이 증가합니다. @lob로 매핑하고 마이그레이션을 동반하세요.
- JPA 이식성:
@Lob(+@Column(name="content")) 조합이 벤더 중립적입니다. - 운영 필수: 엔티티 변경만으로는 프로덕션 스키마가 바뀌지 않습니다. Flyway/Liquibase 마이그레이션이 반드시 필요합니다.
선호 diff:
- @NotBlank
- @Column(name = "content", columnDefinition = "TEXT")
+ @NotBlank
+ @Lob
+ @Column(name = "content")
private String questionContent;필요 import:
import jakarta.persistence.Lob;마이그레이션 존재 여부 확인 스크립트:
주의:
- 이슈([REFACTOR] ANSWER_RANKING_ENTITY 로직 분리 #90) 원인은 “사용자 답변(content)” 255 제한입니다. 현재 변경은 Ranking Entity의 content입니다. 실제 사용자 답변 테이블(예:
AnswerEntity)의 컬럼 타입도 TEXT/CLOB로 변경되었는지 반드시 확인해주세요. 필요시 마이그레이션 초안 드리겠습니다.
🏁 Script executed:
#!/bin/bash
# 1) Answer(사용자 답변) 및 Ranking의 content 컬럼 타입 변경 반영 여부 점검
# AnswerEntity 매핑에서 content 필드와 컬럼 정의 확인
rg -nP -C2 '(class\s+AnswerEntity\b)|(@Column\([^)]*name\s*=\s*"content"[^)]*\))' --g 'gss-**/*.java'
# "TEXT/CLOB" 정의가 어디에 쓰였는지 검색
rg -nP 'columnDefinition\s*=\s*"TEXT"|@Lob' --g 'gss-**/*'
# 마이그레이션 SQL에 content 타입 변경이 있는지 검색
rg -nP -C2 'ALTER\s+TABLE.+content.+(TEXT|CLOB)|MODIFY\s+COLUMN\s+`?content`?.+(TEXT|LONGTEXT)|TYPE\s+TO\s+(TEXT|CLOB)' --g '**/*.sql'Length of output: 1519
🏁 Script executed:
#!/bin/bash
# 1) AnswerEntity 소스 파일 경로 찾기
rg -lP 'class\s+AnswerEntity' --glob '*.java'
# 2) AnswerEntity 및 AnswerRankingEntity에서 content 컬럼 어노테이션 확인
rg -n '@Column\([^)]*name\s*=\s*"content"[^)]*\)' --glob '*.java'
# 3) @Lob 어노테이션 사용 여부 확인
rg -n '@Lob' --glob '*.java'
# 4) SQL 마이그레이션에서 content 컬럼 타입 변경 반영 여부 확인
rg -nP -C2 'ALTER\s+TABLE.+content.+(TEXT|CLOB)|MODIFY\s+COLUMN.+content.+(TEXT|LONGTEXT)|TYPE\s+TO\s+(TEXT|CLOB)' --glob '*.sql'Length of output: 652
🏁 Script executed:
#!/bin/bash
# 1) AnswerEntity content 필드 매핑 확인
rg -nP -C5 'private\s+String\s+content' gss-domain/src/main/java/com/devoops/jpa/entity/github/answer/AnswerEntity.java
# 2) 프로젝트 내 SQL 파일 목록
find gss-domain -type f -name "*.sql"
# 3) SQL 마이그레이션에서 content 컬럼 변경 반영 검색
rg -nP 'content' --glob 'gss-domain/**/*.sql' -C2Length of output: 578
🏁 Script executed:
#!/bin/bash
# 전체 리포지토리에서 SQL 마이그레이션 파일 확인
find . -type f -name '*.sql'
# SQL 파일에서 content 컬럼 변경 반영 여부 검색
rg -nP 'content' --glob '*.sql' -C2Length of output: 215
@lob 매핑 적용 및 마이그레이션 스크립트 추가 필요
- gss-domain/src/main/java/com/devoops/jpa/entity/github/answer/AnswerRankingEntity.java (29–31):
- @Column(name = "content", columnDefinition = "TEXT") + @Lob + @Column(name = "content")
- gss-domain/src/main/java/com/devoops/jpa/entity/github/answer/AnswerEntity.java (33–34):
- @Column(columnDefinition = "TEXT") + @Lob + @Column(name = "content")
- Flyway/Liquibase 마이그레이션 SQL 파일이 확인되지 않습니다. 기존
content컬럼의 VARCHAR(255) → TEXT/CLOB 변경 스크립트를 반드시 추가하세요. - 필요한 import:
import jakarta.persistence.Lob;
🚩 연관 이슈
close #90
🔂 변경 내역
Summary by CodeRabbit