Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough새로운 University 도메인을 추가하고 CareerNet 외부 API와 통합하여 학교 및 학과 정보를 조회할 수 있는 엔드포인트를 제공합니다. Redis 기반 캐싱을 구현하고 포괄적인 테스트를 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Controller as UniversityController
participant Service as GetUniversityQueryService
participant Cache as Redis Cache
participant Port as UniversityInfoPort
participant Adapter as CareerNetAdapter
participant API as CareerNet API
Client->>Controller: GET /api/v4/university/schools
Controller->>Service: getSchools()
alt Cache Hit
Service->>Cache: 캐시 확인 ("schools")
Cache-->>Service: 캐시된 학교 목록
Service-->>Controller: List<SchoolResponse>
else Cache Miss
Service->>Cache: 캐시 확인 ("schools")
Cache-->>Service: 캐시 미스
Service->>Port: getSchools()
Port->>Adapter: getSchools()
Adapter->>Adapter: 페이지 단위 반복
loop 각 페이지 (totalPages만큼)
Adapter->>API: GET /openapi/getOpenApi (학교)
API-->>Adapter: CareerNetSchoolItem 목록
end
Adapter->>Adapter: CareerNetSchoolItem → SchoolData 매핑
Adapter-->>Port: List<SchoolData>
Port-->>Service: List<SchoolData>
Service->>Service: 한글 우선 정렬
Service->>Service: SchoolData → SchoolResponse 매핑
Service->>Cache: 결과 저장 (TTL: 7일)
Service-->>Controller: List<SchoolResponse>
end
Controller-->>Client: CommonResponse.success(결과, SCHOOL_FIND_ALL_SUCCESS)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/main/kotlin/com/weeth/global/config/CacheConfig.kt (1)
32-36: Kotlin 컬렉션 역직렬화를 위해kotlin.타입 허용 추가 필요Kotlin의
List,Map등은kotlin.collections패키지에 정의되어 있습니다. 현재 설정으로는 캐시된 Kotlin 컬렉션 타입의 역직렬화가 실패할 수 있습니다.♻️ Kotlin 컬렉션 지원 추가
BasicPolymorphicTypeValidator .builder() .allowIfSubType("com.weeth.") .allowIfSubType("java.util.") + .allowIfSubType("kotlin.") .build(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/kotlin/com/weeth/global/config/CacheConfig.kt` around lines 32 - 36, The BasicPolymorphicTypeValidator configuration (built via BasicPolymorphicTypeValidator.builder()) needs to allow Kotlin collection types for proper deserialization; add an allowIfSubType for Kotlin (e.g., "kotlin." or "kotlin.collections.") alongside the existing "com.weeth." and "java.util." entries so cached Kotlin List/Map types can be deserialized correctly.src/test/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryServiceTest.kt (1)
17-25: 정상 케이스 테스트 추가 권장예외 전파 테스트만 있고, 정상적으로 학과 목록을 반환하는 happy path 테스트가 없습니다. 매핑 로직이 올바르게 동작하는지 검증하는 테스트를 추가하면 테스트 커버리지가 향상됩니다.
💡 Happy path 테스트 예시
context("커리어넷 API 정상 응답 시") { it("학과 목록을 MajorResponse로 매핑하여 반환한다") { val majorData = MajorData(name = "컴퓨터공학과", category = "공학계열") val majorResponse = MajorResponse(majorName = "컴퓨터공학과", category = "공학계열") every { careerNetPort.getMajors() } returns listOf(majorData) every { universityMapper.toMajorResponse(majorData) } returns majorResponse queryService.getMajors() shouldBe listOf(majorResponse) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryServiceTest.kt` around lines 17 - 25, Add a happy-path unit test to GetMajorQueryServiceTest that verifies queryService.getMajors() returns mapped MajorResponse objects: stub careerNetPort.getMajors() to return a sample MajorData, stub universityMapper.toMajorResponse(MajorData) to return the expected MajorResponse, then assert that queryService.getMajors() equals the list containing that MajorResponse; ensure you use the existing mocks (careerNetPort, universityMapper) and the same test structure (context/it) as the existing exception test.src/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.kt (2)
19-22: 외부 API 호출에 타임아웃 설정 권장
RestClient에 연결 및 읽기 타임아웃이 설정되어 있지 않습니다. 외부 API 응답 지연 시 요청 스레드가 무기한 블로킹될 수 있습니다.♻️ 타임아웃 설정 예시
// RestClientBuilder 구성 시 HttpClient에 타임아웃 설정 import org.springframework.http.client.SimpleClientHttpRequestFactory val requestFactory = SimpleClientHttpRequestFactory().apply { setConnectTimeout(5000) // 5초 setReadTimeout(10000) // 10초 } restClientBuilder .baseUrl(properties.baseUrl) .requestFactory(requestFactory) .build()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.kt` around lines 19 - 22, The RestClient created in CareerNetAdapter (restClient built from restClientBuilder and properties.baseUrl) lacks connect/read timeouts; set a request factory with sensible connect and read timeouts (e.g., SimpleClientHttpRequestFactory or equivalent HttpClient configuration) and pass it into restClientBuilder.requestFactory(...) before calling build() so external API calls cannot block indefinitely; ensure you reference restClientBuilder.requestFactory(...) when modifying the restClient construction.
40-45:totalCount파싱 실패 시 경고 로그 추가 고려
totalCount?.toIntOrNull() ?: 0으로 파싱 실패 시 기본값 0을 사용하면 첫 페이지만 반환되고 나머지 데이터가 누락됩니다. 이 상황은 조용히 발생하므로 디버깅이 어려울 수 있습니다.♻️ 경고 로그 추가 제안
private fun <T : CareerNetItem> fetchAllPages(fetchPage: (Int) -> List<T>): List<T> { val firstPage = fetchPage(1) - val totalCount = firstPage.firstOrNull()?.totalCount?.toIntOrNull() ?: 0 + val rawTotalCount = firstPage.firstOrNull()?.totalCount + val totalCount = rawTotalCount?.toIntOrNull() ?: run { + log.warn("totalCount 파싱 실패: {}", rawTotalCount) + 0 + } val totalPages = ((totalCount + PER_PAGE - 1) / PER_PAGE).coerceAtLeast(1) return firstPage + (2..totalPages).flatMap(fetchPage) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.kt` around lines 40 - 45, In fetchAllPages, totalCount is parsed with firstPage.firstOrNull()?.totalCount?.toIntOrNull() ?: 0 which silently falls back to 0 and can drop pages; change this to detect parse failure by checking toIntOrNull() result and, when null, emit a warning (using the module's logger) including the offending totalCount string and context (e.g., firstPage size or sample item) so debugging is easier; then compute totalPages using the validated integer (or fallback) and proceed to fetch remaining pages as before.src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetSchoolQueryService.kt (1)
9-15:@Transactional(readOnly = true)어노테이션 누락코딩 가이드라인에 따르면 Query UseCase는
@Transactional(readOnly = true)어노테이션이 필요합니다. 현재 DB 접근은 없지만, 일관성 유지와 향후 확장을 위해 추가를 권장합니다.♻️ 수정 제안
import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional `@Service` +@Transactional(readOnly = true) class GetSchoolQueryService(코딩 가이드라인 근거: "query operations must use
@Transactional(readOnly=true) and return DTOs"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetSchoolQueryService.kt` around lines 9 - 15, Add the missing `@Transactional`(readOnly = true) to the query use case so it follows the coding guideline: annotate GetSchoolQueryService (or the getSchools() method) with `@Transactional`(readOnly = true) and add the necessary import (org.springframework.transaction.annotation.Transactional); keep the existing `@Service` and `@Cacheable` annotations and ensure the signature and return type (getSchools -> List<SchoolResponse>) remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryService.kt`:
- Around line 9-15: GetMajorQueryService is missing the standard transactional
annotation; add `@Transactional`(readOnly = true) to the GetMajorQueryService
class (the class declaration that contains the getMajors() method) so it follows
other query services (e.g., GetUserQueryService) and ensures read-only
transactional context for the careerNetPort.getMajors() call and mapping via
universityMapper::toMajorResponse.
---
Nitpick comments:
In
`@src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetSchoolQueryService.kt`:
- Around line 9-15: Add the missing `@Transactional`(readOnly = true) to the query
use case so it follows the coding guideline: annotate GetSchoolQueryService (or
the getSchools() method) with `@Transactional`(readOnly = true) and add the
necessary import (org.springframework.transaction.annotation.Transactional);
keep the existing `@Service` and `@Cacheable` annotations and ensure the signature
and return type (getSchools -> List<SchoolResponse>) remain unchanged.
In
`@src/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.kt`:
- Around line 19-22: The RestClient created in CareerNetAdapter (restClient
built from restClientBuilder and properties.baseUrl) lacks connect/read
timeouts; set a request factory with sensible connect and read timeouts (e.g.,
SimpleClientHttpRequestFactory or equivalent HttpClient configuration) and pass
it into restClientBuilder.requestFactory(...) before calling build() so external
API calls cannot block indefinitely; ensure you reference
restClientBuilder.requestFactory(...) when modifying the restClient
construction.
- Around line 40-45: In fetchAllPages, totalCount is parsed with
firstPage.firstOrNull()?.totalCount?.toIntOrNull() ?: 0 which silently falls
back to 0 and can drop pages; change this to detect parse failure by checking
toIntOrNull() result and, when null, emit a warning (using the module's logger)
including the offending totalCount string and context (e.g., firstPage size or
sample item) so debugging is easier; then compute totalPages using the validated
integer (or fallback) and proceed to fetch remaining pages as before.
In `@src/main/kotlin/com/weeth/global/config/CacheConfig.kt`:
- Around line 32-36: The BasicPolymorphicTypeValidator configuration (built via
BasicPolymorphicTypeValidator.builder()) needs to allow Kotlin collection types
for proper deserialization; add an allowIfSubType for Kotlin (e.g., "kotlin." or
"kotlin.collections.") alongside the existing "com.weeth." and "java.util."
entries so cached Kotlin List/Map types can be deserialized correctly.
In
`@src/test/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryServiceTest.kt`:
- Around line 17-25: Add a happy-path unit test to GetMajorQueryServiceTest that
verifies queryService.getMajors() returns mapped MajorResponse objects: stub
careerNetPort.getMajors() to return a sample MajorData, stub
universityMapper.toMajorResponse(MajorData) to return the expected
MajorResponse, then assert that queryService.getMajors() equals the list
containing that MajorResponse; ensure you use the existing mocks (careerNetPort,
universityMapper) and the same test structure (context/it) as the existing
exception test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 25e59633-6901-4be2-bb89-ece5ff0038fe
📒 Files selected for processing (22)
.claude/rules/api-design.mdbuild.gradle.ktssrc/main/kotlin/com/weeth/domain/university/application/dto/response/MajorResponse.ktsrc/main/kotlin/com/weeth/domain/university/application/dto/response/SchoolResponse.ktsrc/main/kotlin/com/weeth/domain/university/application/exception/CareerNetApiException.ktsrc/main/kotlin/com/weeth/domain/university/application/exception/UniversityErrorCode.ktsrc/main/kotlin/com/weeth/domain/university/application/mapper/UniversityMapper.ktsrc/main/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryService.ktsrc/main/kotlin/com/weeth/domain/university/application/usecase/query/GetSchoolQueryService.ktsrc/main/kotlin/com/weeth/domain/university/domain/model/MajorData.ktsrc/main/kotlin/com/weeth/domain/university/domain/model/SchoolData.ktsrc/main/kotlin/com/weeth/domain/university/domain/port/CareerNetPort.ktsrc/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.ktsrc/main/kotlin/com/weeth/domain/university/presentation/UniversityController.ktsrc/main/kotlin/com/weeth/domain/university/presentation/UniversityResponseCode.ktsrc/main/kotlin/com/weeth/global/config/CacheConfig.ktsrc/main/kotlin/com/weeth/global/config/SecurityConfig.ktsrc/main/kotlin/com/weeth/global/config/SwaggerConfig.ktsrc/main/kotlin/com/weeth/global/config/properties/CareerNetProperties.ktsrc/main/resources/application.ymlsrc/test/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryServiceTest.ktsrc/test/kotlin/com/weeth/domain/university/application/usecase/query/GetSchoolQueryServiceTest.kt
| @Service | ||
| class GetMajorQueryService( | ||
| private val careerNetPort: CareerNetPort, | ||
| private val universityMapper: UniversityMapper, | ||
| ) { | ||
| @Cacheable(value = ["majors"], key = "'all'") | ||
| fun getMajors(): List<MajorResponse> = careerNetPort.getMajors().map(universityMapper::toMajorResponse) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if other query services without DB access also omit `@Transactional`
# Expected: Find patterns of `@Transactional` usage in query services
rg -n "@Transactional" --type=kt -C2 | grep -i "queryservice" || echo "No matches found"Repository: Team-Weeth/weeth-server
Length of output: 127
🏁 Script executed:
# Find Kotlin files and check UseCase/Service patterns
find src -name "*.kt" -type f | grep -E "(usecase|service)" | head -20Repository: Team-Weeth/weeth-server
Length of output: 1881
🏁 Script executed:
# Search for `@Transactional` usage with proper Kotlin file detection
rg "@Transactional" --type-add 'kt:*.kt' -t kt -C2 | head -100Repository: Team-Weeth/weeth-server
Length of output: 9732
🏁 Script executed:
# Look at the actual class definition and surrounding context
cat -n src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryService.ktRepository: Team-Weeth/weeth-server
Length of output: 847
🏁 Script executed:
# Find other query services to understand the pattern
fd "QueryService\.kt$" srcRepository: Team-Weeth/weeth-server
Length of output: 1457
@Transactional(readOnly = true) 어노테이션 추가 필요
모든 QueryService 클래스는 일관되게 @Transactional(readOnly = true) 어노테이션이 적용됩니다 (GetUserQueryService, GetSessionQueryService, GetScheduleQueryService 등). GetMajorQueryService도 동일한 패턴을 따라야 합니다.
외부 API 호출이라도 쿼리 작업의 표준 패턴을 유지하기 위해 어노테이션을 추가하세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetMajorQueryService.kt`
around lines 9 - 15, GetMajorQueryService is missing the standard transactional
annotation; add `@Transactional`(readOnly = true) to the GetMajorQueryService
class (the class declaration that contains the getMajors() method) so it follows
other query services (e.g., GetUserQueryService) and ensures read-only
transactional context for the careerNetPort.getMajors() call and mapping via
universityMapper::toMajorResponse.
hyxklee
left a comment
There was a problem hiding this comment.
고생하셨습니다! 코멘트 몇가지만 확인해주세용
그리고 노션의 "학교학과API 연동" 문서에 우리 API를 사용한 전체 응답 내용을 문서화해주시면 감사하겠습니다! 다같이 보면서 얘기해봐야 좋을 것 같아용
| import com.weeth.domain.university.domain.model.MajorData | ||
| import com.weeth.domain.university.domain.model.SchoolData | ||
|
|
||
| interface CareerNetPort { |
There was a problem hiding this comment.
커리어넷은 어떻게 보면 "학교, 학과 정보를 가져오기 위한 구현체"이니까 포트명이 되기 보다는 어댑터를 현재 이름처럼 쓰고, 포트 명은 조금 더 제너럴한 명칭으로 바꾸는건 어떨까용??
추후에 커리어넷보다 나은 어댑터가 있으면 갈아끼울 수도 있으니까요!
| import io.mockk.every | ||
| import io.mockk.mockk | ||
|
|
||
| class GetSchoolQueryServiceTest : |
There was a problem hiding this comment.
캐시 히트 관련된 통합 테스트가 있으면 좋을 것 같아요!
캐시 히트는 실제로 잘 되는지, API 응답이 캐싱이 있는 것과 없는 것에서 어느정도 차이가 나는지를 문서화해두면 좋을 것 같아염
API 호출을 너무 많이 하면 차단될 수도 있으니 10-50건 정도만..?
|
|
||
| @Tag(name = "UNIVERSITY", description = "학교/학과 API") | ||
| @RestController | ||
| @RequestMapping("/api/v4") |
There was a problem hiding this comment.
university 의미도 엔드포인트에 담기면 좋겠네요!
| import org.springframework.stereotype.Service | ||
|
|
||
| @Service | ||
| class GetMajorQueryService( |
There was a problem hiding this comment.
서비스 하나로 합치는 건 어떨까요?? 서비스가 비대해질 가능성도 적고 응집도를 높여서 관리하는게 좋아보여욥
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/test/resources/application-test.yml (1)
7-15: OSIV 설정 누락 (코딩 가이드라인)이 파일은
**/application*.yml패턴에 매칭되며, 코딩 가이드라인에 따르면 OSIV(Open Session In View)가 비활성화되어야 합니다. 현재spring.jpa.open-in-view설정이 없어 Spring Boot 기본값인true(활성화)가 적용됩니다.제안: OSIV 비활성화 설정 추가
spring: data: redis: host: localhost port: 6379 password: jpa: hibernate: ddl-auto: create-drop show-sql: true + open-in-view: false properties: hibernate: format_sql: true코딩 가이드라인 참조:
**/application*.yml: OSIV (Open Session In View) must be disabled in application configuration (spring.jpa.open-in-view: false)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/resources/application-test.yml` around lines 7 - 15, The application YAML for tests is missing the OSIV setting so Spring Boot defaults to true; add the property spring.jpa.open-in-view: false to the application*.yml (the same file containing jpa.hibernate.ddl-auto, show-sql, etc.) to explicitly disable Open Session In View and comply with the coding guideline.src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryService.kt (1)
10-14:@Transactional(readOnly = true)누락코딩 가이드라인에 따르면 쿼리 작업은
@Transactional(readOnly = true)를 적용해야 합니다. 현재 외부 API 호출만 수행하므로 DB 트랜잭션이 필수는 아니지만, 향후 확장성과 일관성을 위해 추가를 권장합니다.♻️ 제안 수정
import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional `@Service` +@Transactional(readOnly = true) class GetUniversityQueryService(As per coding guidelines: "query operations must use
@Transactional(readOnly=true) and return DTOs"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryService.kt` around lines 10 - 14, Add `@Transactional`(readOnly = true) to the GetUniversityQueryService class declaration (importing org.springframework.transaction.annotation.Transactional) so all query methods run with a read-only transaction; update any public query methods in GetUniversityQueryService to return DTO types (not entities) via UniversityMapper to comply with the “queries return DTOs” guideline.src/test/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryServiceTest.kt (1)
11-36: Happy path 테스트 추가 권장현재 예외 전파 테스트만 존재합니다. 다음 시나리오에 대한 테스트 추가를 권장합니다:
- 정상 데이터 반환 및 매핑 검증
- 한글 우선 정렬 로직 검증
- 빈 리스트 반환 케이스
📝 테스트 추가 예시
context("정상 호출 시") { it("한글 이름이 영문보다 먼저 정렬된다") { val mixedData = listOf( SchoolData("ABC대학교", "서울"), SchoolData("가나대학교", "부산") ) every { universityInfoPort.getSchools() } returns mixedData every { universityMapper.toSchoolResponse(any()) } answers { SchoolResponse(firstArg<SchoolData>().name, firstArg<SchoolData>().region) } val result = queryService.getSchools() result[0].schoolName shouldBe "가나대학교" result[1].schoolName shouldBe "ABC대학교" } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryServiceTest.kt` around lines 11 - 36, Add happy-path unit tests in GetUniversityQueryServiceTest for queryService.getSchools and queryService.getMajors: mock universityInfoPort to return sample lists (including mixed Korean and English names and an empty list), mock universityMapper.toSchoolResponse / toMajorResponse to map SchoolData/MajorData -> SchoolResponse/MajorResponse, then assert correct mapping, that Korean-named entries sort before English (e.g., "가나대학교" before "ABC대학교"), and that an empty list is handled and returns an empty response list; also include a normal non-exception return case for getMajors similarly.src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityCacheIntegrationTest.kt (1)
66-73: 캐시 데이터 동등성 검증 강화 고려
first shouldBe second비교가 참조가 아닌 값 동등성을 검증하는지 확인이 필요합니다. 데이터 클래스라면 자동으로equals가 구현되어 문제없지만, JSON 역직렬화 후에도 동일한지 검증하려면 내용 비교를 명시적으로 추가할 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityCacheIntegrationTest.kt` around lines 66 - 73, The current test relies on "first shouldBe second" but to guarantee structural/value equality even after JSON round-trip, add an explicit deep-content comparison: call getUniversityQueryService.getSchools() into first and second (already present) and then compare their serialized JSON representations (e.g., via a shared ObjectMapper.writeValueAsString(first) vs writeValueAsString(second)) or otherwise perform a field-by-field equality check; also optionally assert they are not the same instance (first shouldNotBeSameInstanceAs second) to ensure caching returns equivalent data, not the identical object.src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt (1)
50-51: 벤치마크 단정식이 외부 네트워크 지연에 취약합니다실 API 기반 성능 테스트에서 단순
<비교는 일시적 네트워크 변동으로 flaky 해질 수 있습니다. 임계값(예: N% 개선) 또는 다회 측정의 중앙값 기준으로 비교하면 CI 안정성이 좋아집니다.Also applies to: 66-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt` around lines 50 - 51, The current assertion in UniversityRealApiCacheTest (com.weeth.domain.university.application.usecase.query.UniversityRealApiCacheTest) uses a raw less-than check on result.totalWithCacheMs vs result.estimatedTotalWithoutCacheMs which is flaky due to external network variance; change the test to either (A) require a relative improvement threshold (e.g., assert totalWithCacheMs <= estimatedTotalWithoutCacheMs * 0.9) using the existing variables result.totalWithCacheMs and result.estimatedTotalWithoutCacheMs, or (B) perform multiple measurements for both cached and uncached runs, compute the median (or mean after trimming) and compare medians, and apply a small relative threshold to determine success; apply the same fix to the similar assertion around lines 66-67.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt`:
- Line 31: The current check sets hasRealApiKey by only testing
System.getenv("CAREER_NET_API_KEY") != null, which treats an empty string as a
valid key; change the logic in UniversityRealApiCacheTest to verify the env var
is non-null and not blank (e.g., trim and check not empty) before enabling
real-API tests so empty or whitespace-only CAREER_NET_API_KEY values do not
activate live calls.
---
Nitpick comments:
In
`@src/main/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryService.kt`:
- Around line 10-14: Add `@Transactional`(readOnly = true) to the
GetUniversityQueryService class declaration (importing
org.springframework.transaction.annotation.Transactional) so all query methods
run with a read-only transaction; update any public query methods in
GetUniversityQueryService to return DTO types (not entities) via
UniversityMapper to comply with the “queries return DTOs” guideline.
In
`@src/test/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryServiceTest.kt`:
- Around line 11-36: Add happy-path unit tests in GetUniversityQueryServiceTest
for queryService.getSchools and queryService.getMajors: mock universityInfoPort
to return sample lists (including mixed Korean and English names and an empty
list), mock universityMapper.toSchoolResponse / toMajorResponse to map
SchoolData/MajorData -> SchoolResponse/MajorResponse, then assert correct
mapping, that Korean-named entries sort before English (e.g., "가나대학교" before
"ABC대학교"), and that an empty list is handled and returns an empty response list;
also include a normal non-exception return case for getMajors similarly.
In
`@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityCacheIntegrationTest.kt`:
- Around line 66-73: The current test relies on "first shouldBe second" but to
guarantee structural/value equality even after JSON round-trip, add an explicit
deep-content comparison: call getUniversityQueryService.getSchools() into first
and second (already present) and then compare their serialized JSON
representations (e.g., via a shared ObjectMapper.writeValueAsString(first) vs
writeValueAsString(second)) or otherwise perform a field-by-field equality
check; also optionally assert they are not the same instance (first
shouldNotBeSameInstanceAs second) to ensure caching returns equivalent data, not
the identical object.
In
`@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt`:
- Around line 50-51: The current assertion in UniversityRealApiCacheTest
(com.weeth.domain.university.application.usecase.query.UniversityRealApiCacheTest)
uses a raw less-than check on result.totalWithCacheMs vs
result.estimatedTotalWithoutCacheMs which is flaky due to external network
variance; change the test to either (A) require a relative improvement threshold
(e.g., assert totalWithCacheMs <= estimatedTotalWithoutCacheMs * 0.9) using the
existing variables result.totalWithCacheMs and
result.estimatedTotalWithoutCacheMs, or (B) perform multiple measurements for
both cached and uncached runs, compute the median (or mean after trimming) and
compare medians, and apply a small relative threshold to determine success;
apply the same fix to the similar assertion around lines 66-67.
In `@src/test/resources/application-test.yml`:
- Around line 7-15: The application YAML for tests is missing the OSIV setting
so Spring Boot defaults to true; add the property spring.jpa.open-in-view: false
to the application*.yml (the same file containing jpa.hibernate.ddl-auto,
show-sql, etc.) to explicitly disable Open Session In View and comply with the
coding guideline.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 94895c3e-c58c-4585-8545-12853156bd27
📒 Files selected for processing (12)
.gitignoresrc/main/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryService.ktsrc/main/kotlin/com/weeth/domain/university/domain/port/UniversityInfoPort.ktsrc/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.ktsrc/main/kotlin/com/weeth/domain/university/presentation/UniversityController.ktsrc/main/kotlin/com/weeth/global/config/SecurityConfig.ktsrc/test/kotlin/com/weeth/config/CacheBenchmarkUtil.ktsrc/test/kotlin/com/weeth/config/TestContainersConfig.ktsrc/test/kotlin/com/weeth/domain/university/application/usecase/query/GetUniversityQueryServiceTest.ktsrc/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityCacheIntegrationTest.ktsrc/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.ktsrc/test/resources/application-test.yml
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/kotlin/com/weeth/domain/university/infrastructure/CareerNetAdapter.kt
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt (1)
50-50: 성능 단정식이 CI/네트워크 지터에 플래키할 수 있습니다.Line 50, Line 66의
strict less-than단정은 외부 API 상태나 순간 지연에 민감합니다. 임계값(예: 최소 개선율) 기반 비교나 반복 중앙값 기준으로 바꾸면 안정성이 좋아집니다.Also applies to: 66-66
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt` at line 50, The current strict assertions in UniversityRealApiCacheTest (comparing result.totalWithCacheMs and result.estimatedTotalWithoutCacheMs using shouldBeLessThan at lines referenced) are flaky under CI/network jitter; modify the test to assert a minimum improvement margin or use a robust statistic: either require that (estimatedTotalWithoutCacheMs - totalWithCacheMs) exceeds a configurable threshold or percent (e.g., >= X ms or >= Y%), or run the measurement multiple times and compare medians instead of single-shot values; update the assertions to check for that threshold/median-based improvement rather than a strict less-than to stabilize the test.src/main/kotlin/com/weeth/global/config/CacheConfig.kt (1)
43-43: TTL 값을 설정 파일로 외부화하는 것이 좋습니다.Line 43의
Duration.ofDays(7)하드코딩은 정책 변경(예: 24h/7d) 시 코드 수정이 필요해 운영 유연성이 떨어집니다.application-*.yml기반Duration프로퍼티로 분리해 환경별 조정 가능하게 해주세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/kotlin/com/weeth/global/config/CacheConfig.kt` at line 43, The hardcoded TTL Duration.ofDays(7) in CacheConfig should be externalized to a configuration property; add a Duration property (e.g., cache.ttl) in application-*.yml, bind it into CacheConfig via `@Value` or a `@ConfigurationProperties` class (use java.time.Duration type), and replace the literal Duration.ofDays(7) in the entryTtl(...) call with the injected Duration field (refer to CacheConfig and the entryTtl usage to make the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/kotlin/com/weeth/global/config/CacheConfig.kt`:
- Around line 31-38: The current objectMapper.copy().activateDefaultTyping(...)
call uses BasicPolymorphicTypeValidator.allowIfSubType("java.util.") which is
too broad and can enable gadget-chain RCE; update the validator used in
activateDefaultTyping to whitelist only specific collection classes you actually
need (e.g., use allowIfSubClass(ArrayList.class),
allowIfSubClass(LinkedList.class), allowIfSubClass(HashMap.class), etc., instead
of the "java.util." prefix) or alternatively remove broad polymorphic activation
and explicitly manage polymorphic types via `@JsonTypeInfo/`@JsonSubTypes or by
declaring concrete cache value types; adjust the code around
objectMapper.copy().activateDefaultTyping and BasicPolymorphicTypeValidator
accordingly.
---
Nitpick comments:
In `@src/main/kotlin/com/weeth/global/config/CacheConfig.kt`:
- Line 43: The hardcoded TTL Duration.ofDays(7) in CacheConfig should be
externalized to a configuration property; add a Duration property (e.g.,
cache.ttl) in application-*.yml, bind it into CacheConfig via `@Value` or a
`@ConfigurationProperties` class (use java.time.Duration type), and replace the
literal Duration.ofDays(7) in the entryTtl(...) call with the injected Duration
field (refer to CacheConfig and the entryTtl usage to make the change).
In
`@src/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt`:
- Line 50: The current strict assertions in UniversityRealApiCacheTest
(comparing result.totalWithCacheMs and result.estimatedTotalWithoutCacheMs using
shouldBeLessThan at lines referenced) are flaky under CI/network jitter; modify
the test to assert a minimum improvement margin or use a robust statistic:
either require that (estimatedTotalWithoutCacheMs - totalWithCacheMs) exceeds a
configurable threshold or percent (e.g., >= X ms or >= Y%), or run the
measurement multiple times and compare medians instead of single-shot values;
update the assertions to check for that threshold/median-based improvement
rather than a strict less-than to stabilize the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 85854156-0be4-4036-b21b-27698de62178
📒 Files selected for processing (2)
src/main/kotlin/com/weeth/global/config/CacheConfig.ktsrc/test/kotlin/com/weeth/domain/university/application/usecase/query/UniversityRealApiCacheTest.kt
|
|
||
| companion object { | ||
| private const val MYSQL_IMAGE = "mysql:8.0.41" | ||
| private const val MYSQL_IMAGE = "mysql:8.0.36" |
There was a problem hiding this comment.
제 로컬에선 41버전이 안 돌아가서 다운그레이드시켰는데 잘못하고 올린것 같아요 ㅠ.. 다시 바꿔두겠습니당
| - 각 테스트 전 캐시를 초기화하여 테스트 간 간섭을 방지합니다. | ||
| - 성능 벤치마크는 UniversityRealApiCacheTest에서 실제 API를 사용해 측정합니다. | ||
| */ | ||
| @SpringBootTest |
There was a problem hiding this comment.
각 테스트 결과들 노션에 문서화 해주시면 감사하겠습니다!
There was a problem hiding this comment.
네! 학교/학과 api 페이지에 업데이트해두겟습니다!!
* [WTH-137] weeth server cicd 구축 (#4) * chore: 깃 이그노어 업데이트 * deploy: Dockerfile 통합 * deploy: main, dev 통합 CI 워크플로우 설정 * deploy: docker ignore 설정 * deploy: 개발서버 CICD 적용 * deploy: 운영서버 CICD 적용 * chore: caddy 리버스 프록시 정보 추가 * chore: 릴리즈 드래프터 적용 * chore: lint 수정 * chore: PR 템플릿 수정 * deploy: key 볼륨 마운트 설정 * deploy: 워크플로우 의존성 설정 * deploy: ci job 이름 변경 * deploy: wrapping 제거 * deploy: 홈 디렉토리 설정 변경 * chore: jdk 21로 업데이트 * [WTH-140] Deploy Job 분리 (#5) * deploy: job 분리 * chore: 개행 추가 * hotfix: 레디스 설정 추가 * [WTH-143] 응답 코드 및 예외 예시 오류 수정 (#7) * fix: 커스텀 예외 코드 반환 * fix: 그룹 페이지에 각각 커스터마이저 등록 * [WTH-139] file 도메인 코틀린 마이그레이션 (#6) * refactor: File 엔티티 구조 변경 및 마이그레이션 * refactor: 기존 서비스 제거 * refactor: FileRepository 마이그레이션 * refactor: 예외 마이그레이션 * refactor: 매퍼 마이그레이션 * refactor: dto 마이그레이션 * refactor: 파일 엔티티 개선을 위한 Enum Class 추가 * refactor: 타 도메인 조회를 위한 Reader 인터페이스 추가 * refactor: 파일 업로드 URL 생성을 위한 Port/Adapter 구현 * refactor: 기존 유스케이스를 QueryService로 이전 * refactor: Controller 마이그레이션 * refactor: File 도메인 변경에 따른 기존 코드 수정 * refactor: File 도메인 변경에 따른 기존 코드 수정 * refactor: File 도메인 변경에 따른 기존 코드 수정 * refactor: port/adapter 컨벤션 수정 및 스웨거 노출 API prefix 제거 * refactor: 예외 발생 원인 유지 * test: 테스트 추가 * feat: S3/CDN url 반환 로직 구현 * refactor: 주석 추가 * feat: S3/CDN url 반환 로직 구현 * feat: S3/CDN url 반환 환경변수 설정 * refactor: 주석 위치 수정 * refactor: 하위호환 * test: s3 presigned url 테스트 추가 * feat: VO 분리 * refactor: 미사용 객체 제거 * refactor: VO 규격에 맞게 테스트 대응 * refactor: lint 설정 * refactor: Repository 테스트 구현 및 불필요 코드 정리 * refactor: code-review skill 오류 해결 * refactor: 단일 enum 파일로 확장자 관리 * refactor: lint 설정 * refactor: 수정시 null check 추가 * refactor: command로 이동 * refactor: 입력 검증 수정 * [WTH-144] comment 도메인 코틀린 마이그레이션 (#8) * refactor: Comment 엔티티 마이그레이션 * refactor: 기존 서비스 제거 * refactor: Repository 마이그레이션 * refactor: Dto 마이그레이션 및 분리 * refactor: 예외 마이그레이션 * refactor: mapper 마이그레이션 * refactor: 유스케이스 마이그레이션 및 통합 * refactor: 컨트롤러 마이그레이션 * refactor: Comment Test Fixture 추가 * refactor: Comment 변경으로 인한 notice 도메인 수정 * refactor: Comment 변경으로 인한 post 도메인 수정 * refactor: 주석 추가 * refactor: 롬복 어노테이션 추가 * refactor: Increse 메서드 추가 * feat: 테스트 시 쿼리 측정을 위한 설정 추가 * feat: 댓글 조회 서비스 구현 * refactor: 댓글 조회 시 파일도 bulk로 조회해오도록 수저 * chore: 컨텍스트 업데이트 * refactor: 자체 리뷰 사항 반영 * feat: 레거시 vs 개선 로직 비교 테스트 추가 * chore: lint 처리 * [WTH-146] penalty 도메인 코틀린 마이그레이션 (#10) * refactor: Penalty enum java -> kotlin으로 폴더 변경 * refactor: Penalty enum kotlin으로 문법 변환 * refactor: Penalty entity, repository java -> kotlin으로 폴더 변경 * refactor: Penalty dto, mapper java -> kotlin으로 폴더 변경 * refactor: Penalty entity, repository kotlin으로 문법 변환 * refactor: Penalty dto, mapper kotlin으로 문법 변환 * build: Lombok 의존성 및 Kotlin 호환성 설정 추가 * refactor: Kotlin 마이그레이션에 따른 Java 호출 수정 * refactor: Penalty exception java -> kotlin으로 폴더 변경 * refactor: Penalty exception kotlin으로 문법 변환 * refactor: Penalty usecase java -> kotlin으로 폴더 변경 * refactor: Penalty service 불필요한 파일 삭제 * refactor: Penalty controller, responsecode java -> kotlin으로 폴더 변경 * refactor: 코틀린 문법에 맞게 Penalty errorcode 수정 * refactor: Penalty usecase kotlin으로 문법 변환 * refactor: Penalty controller, responseCode kotlin으로 문법 변환 * refactor: PenaltyType enum 패키지 이동 * refactor: Penalty DTO 개별 파일로 분리 * refactor: Penalty usecase command/query 분리 * refactor: Penalty mapper, controller 새 구조에 맞게 수정 * refactor: GetPenaltyQueryService 조회 N+1 쿼리 최적화 * refactor: Penalty 카운트 수정 시 User 비관적 락 적용 * fix: SavePenaltyUseCase 비관적 락 정확성 우선시하여 stale read 방지 * refactor: GetPenaltyQueryService UserCardinal N+1 쿼리 배치 조회로 최적화 * refactor: PenaltyRepository 동적 쿼리를 명시적 @query로 전환 * fix: PenaltyResponse에 항상 null 반환되던 필드에 실제 값 매핑 * refactor: Penalty UseCase 메서드명 execute를 도메인 동작명으로 변경 * refactor: Penalty DTO 불필요한 nullable 제거 및 Mapper 파라미터 정리 * refactor: QueryService DTO 조합 로직 Mapper로 이동 및 메서드명 개선 * style: Penalty DTO에 Swagger @Schema 어노테이션 추가 * refactor: Penalty 쿼리 표준화 및 LAZY fetch 전환 * refactor: QueryService @transactional 어노테이션 클래스 레벨로 이동 * [WTH-142] attendance 도메인 코틀린 마이그레이션 (#9) * refactor: Attendacne domain java -> kotlin으로 폴더 변경 * build: Lombok 의존성 및 Kotlin 호환성 설정 추가 * refactor: Attendacne entity kotlin으로 문법 변환 * test: Attendance entity Test 추가 * refactor: Attendacne enum java -> kotlin으로 폴더 변경 * refactor: Attendacne eum kotlin으로 문법 변환 * refactor: Attendacne repository kotlin으로 문법 변환 * refactor: Attendacne dto java -> kotlin으로 폴더 변경 * refactor: Attendacne dto kotlin으로 문법 변환 * refactor: Attendacne dto 코틀린 코드에 맞춰 수정 * refactor: Attendacne mapper java -> kotlin으로 폴더 변경 및 dto 확장 준비 * refactor: Attendance DTO request/response로 분리 * refactor: Attendacne mapper 코틀린 코드에 맞춰 참조 코드 수정 * refactor: Attendacne service java -> kotlin으로 폴더 변경 * refactor: Attendacne service kotlin으로 문법 변환 * refactor: Attendacne usecase java -> kotlin으로 폴더 변경 * refactor: Attendacne usecase kotlin으로 문법 변환 * refactor: Attendacne exception java -> kotlin으로 폴더 변경 * refactor: Attendacne exception kotlin으로 문법 변환 * refactor: Attendacne controller, responseCode java -> kotlin으로 폴더 변경 * refactor: Attendacne controller, responseCode kotlin으로 문법 변환 * refactor: AttendanceScheduler를 service/scheduler 패키지로 이동 * refactor: Attendance UseCase command/query 분리 * test: Attendance command/query 분리에 따른 테스트 재작성 * refactor: Attendance controller UseCase 의존 분리 * test: AttendanceMapperTest 하드코딩 값을 변수 참조로 변경 * refactor: 테스트 FQCN 정리 및 하드코딩 값 변수 참조로 변경 * refactor: Attendance Schduler Transaction import jakarta에서 spring으로 변경 * refactor: Attendance N+1 방지 위해 @manytoone(fetch = FetchType.LAZY) 적용 * refactor: Attendance 단건 조회 N+1 방지 위해 JOIN FETCH 적용 * test: UpdateAttendanceStatusUseCase 테스트 findByIdWithUser 반영 * refactor: user.attendances 조회를 Repository 쿼리로 분리 * test: Repository 쿼리 전환에 따른 테스트 mock 수정 * refactor: AttendanceMapper 중복 메서드 통합 * refactor: AttendanceGetService 미사용 메서드 제거 * refactor: Attendance DTO에 @Schema 어노테이션 추가 * refactor: AttendanceMainResponse를 AttendanceSummaryResponse로 이름 변경 * refactor: UpdateAttendanceStatusRequest 불필요한 @NotNull 제거 * refactor: toMainResponse를 toSummaryResponse로 변경 * stye: kotlin 코드 포맷 적용 * test: toSummaryResponse mock에 isAdmin 파라미터 명시 * refactor: UseCase 메서드명 execute를 도메인 용어로 변경 * refactor: GetAttendanceQueryService 메서드명을 도메인 용어로 변경 * refactor: AttendanceScheduler를 infrastructure로 이동, Usecase로 분리 * refactor: 아키텍처 기준에 맞게 attendance enum 패키지 구조 수정 * [WTH-145] board 도메인 마이그레이션 (#11) * [WTH-157] global 도메인 마이그레이션 (#13) * refactor: JWT 마이그레이션 * refactor: Apple 마이그레이션 * refactor: Kakao 마이그레이션 * refactor: Config 마이그레이션 * refactor: 예외 관련 마이그레이션 * refactor: 스프링 인증 관련 마이그레이션 * refactor: Redis 토큰 저장소 마이그레이션 * refactor: 기타 global 파일 마이그레이션 * refactor: 유저 도메인 변경사항 전파 * docs: 주석 추가 * docs: 코드 스타일 업데이트 * docs: 마크다운 깨짐 해결 * chore: lint 수정 * refactor: Role -> String 작업 원복 * refactor: 리뷰 반영 * refactor: 토큰 오류 수정 * refactor: email nullable 설정 * [WTH-149] account 도메인 코틀린 마이그레이션 (#12) * test: account 도메인 안전망 테스트 추가 * refactor: Account entity java -> kotlin 패키지 변경 * refactor: Account entity kotlin으로 문법 변경, money vo 추가 * refactor: entity 코틀린 문법 변경에 따른 참조 파일 수정 * refactor: Account dto, mapper java -> kotlin 패키지 변경 * refactor: Account dto 분리 및 kotlin 문법으로 변환 * refactor: Account mapper 수동 mapper, kotlin 문법으로 변환 * refactor: dto, mapper 코틀린 문법 변경에 따른 참조 파일 수정 * refactor: Account repository java -> kotlin 패키지 변경 * refactor: Account repository kotlin 문법으로 변환 * refactor: AccountUseCaseImpl find() 조회 N+1 개선 * refactor: Account repository 코틀린 문법 변경에 따른 참조 파일 수정 * refactor: 불필요한 service, usecase 관련 파일 삭제 * refactor: 아키텍처 구조에 맞게 usecase command/query로 분리 * test: usecase command/query 분리에 맞춰 테스트 다시 작성 * refactor: Account usecase 분리에 맞춰 참조 파일 수정 * refactor: Account exception, controller, responsecode java -> kotlin 패키지 변경 * refactor: Account controller, responsecode kotlin 문법으로 변환 * refactor: Account exception kotlin 문법으로 변환 * refactor: 불필요한 @JvmStatic 어노테이션 제거 * fix: Receipt.update() 금액 유효성 검사 누락 수정 * fix: updateReceipt 기수 존재 검증 누락 수정 및 description @notblank 추가 * fix: 영수증 수정 시 빈 파일 리스트로 전체 삭제 처리 * refactor: 요청 스웨거 예시 수정 * refactor: 요청 스웨거 예시 추가 * fix: Receipt 수정 시 account 불일치 검증 및 clearMocks 누락 수정 * [WTH-159] user 도메인 마이그레이션 (#14) * refactor: 사용자 도메인 모델을 코틀린으로 전환 * refactor: 사용자 DTO와 컨트롤러를 재구성 * refactor: 사용자 예외와 조회 로직을 정리 * feat: 소셜 로그인 연동을 구현 * perf: OB 적용 조회를 배치로 최적화 * refactor: 타 도메인 사용자 의존성을 정리 * test: 의존성 변경에 맞춰 테스트를 수정 * test: 사용자 도메인 테스트 구조를 개편 * refactor: 유저 테스트 명시적으로 수정 * refactor: 탈퇴 메서드 이전 * refactor: 미사용 메서드 제거 * refactor: 개행 추가 * refactor: DB 정렬 쿼리로 개선 * refactor: lint 설정 * [WTH-148] schedule 도메인 코틀린 마이그레이션 (#15) * test: Schedule 도메인 안전망 테스트 추가 * refactor: Schedule entity event java -> kotlin 패키지 변경 * refactor: Schedule entity event 독립 entity로 변경 및 kotlin 문법으로 수정 * refactor: Schedule entity event과 관련된 참조 파일 수정 * refactor: Meeting → Session, Attendance kotlin 엔티티 변환 및 attendance 도메인으로 이동 * refactor: Attendance 도메인 참조 파일 Session/AttendanceStatus로 업데이트 * refactor: Schedule 도메인 참조 파일 Session으로 업데이트 * refactor: UserManageUseCase Session 참조로 업데이트 * test: 문법 변환에 맞춰 Schedule 테스트 및 픽스처 수정 * refactor: 미사용 Status enum 제거 * refactor: Schedule enum java -> kotlin 패키지 변경 * refactor: Schedule enum kotlin 문법으로 변환 * refactor: Schedule dto 분리 및 kotlin 문법으로 변환 * refactor: requiredItem 속성 삭제 및 dto 참조 파일 수정 * refactor: Schedule mapper java -> kotlin 패키지 변경 * refactor: Schedule mapper kotlin 문법으로 변환 * refactor: Schedule validator java -> kotlin 패키지 변경, java dto 삭제 * refactor: Schedule validator kotlin 문법으로 변환 * refactor: Schedule UseCase/Service Kotlin DTO 참조로 전환 * refactor: Schedule Controller Kotlin DTO 참조로 전환 * refactor: Schedule mapper/validator Kotlin DTO 참조로 전환 및 테스트 수정 * refactor: Schedule event repository kotlin 문법으로 변환 * refactor: SessionReader 인터페이스 추가 * refactor: User Attendance 연관관계 단순화 및 N+1 해결 * refactor: AttendanceRepository 배치 조회 메서드 추가 * refactor: AttendanceRepository deleteAllBySession 벌크 삭제 추가 * refactor: session, evenet, attendence usecase 생성 * refactor: schedule usecase get query 생성 * refactor: usecase command/query 통합, 분리에 맞춰 참조 파일 수정, 삭제 * refactor: usecase command/query 통합, 분리에 맞춰 test 수정, 삭제 * refactor: SessionRepository/AttendanceRepository 비관적 락 쿼리 추가 * refactor: ManageSessionUseCase/ManageAttendanceUseCase 비관적 락 적용 * refactor: schedule/attendance 컨트롤러 Kotlin 전환 및 Event/Session API 분리 * refactor: ScheduleTimeCheck Kotlin 전환 및 요청 DTO type 필드 제거 * fix: ScheduleTimeCheckValidator null 입력 시 NPE 방지 * refactor: Event 조회를 Command UseCase에서 QueryService로 분리 * style: GetScheduleQueryService Kotlin 관용 표현으로 리팩토링 * style: findById().orElseThrow() Kotlin 스타일로 통일 및 불필요한 줄 제거 * refactor: Session 조회를 Command UseCase에서 GetSessionQueryService로 분리 * style: ktlintFormat 적용 * refactor: Meeting → Session 네이밍 통일 * style: GetAttendanceQueryService mapper 필드명 attendanceMapper로 통일 * docs: QR 코드 출석 기능 예정 메서드에 TODO 주석 추가 * refactor: schedule DTO에 @Schema 어노테이션 추가 * refactor: Meeting → Session 네이밍 통일 * fix: 머지 충돌로 인한 UserManageUseCaseImpl 컴파일 오류 수정 * fix: 출석 상태 관리자 재정 시 상태 검증 및 카운터 보정 로직 수정 * test: UserManageUseCaseTest 의존성을 실제 구현체에 맞게 수정 * fix: SessionNotFoundException을 attendance 도메인으로 분리 * refactor: findSessionInfos 중복 정렬 제거 * refactor: Meeting → Session 네이밍 통일 * fix: Session.updateInfo에 시간 유효성 검증 추가 * fix: Event update에 시간 유효성 검증 추가 * refactor: ScheduleController에서 불필요한 @ApiErrorCodeExample 제거 * refactor: AttendanceRepository mock을 relaxed로 변경하여 saveAll 호출 오류 수정 * refactor: Meeting -> Session 네이밍 변경 * refactor: ResponseCode 코드 번호 정리 * refactor: SessionErrorCode, SessionNotFoundException를 attendance 패키지로 이동 * refactor: content 필드 text -> length 500으로 변경 * refactor: attendance에서 session 패키지로 분리 * refactor: SwaggerConfig 도메인 코드 범위 테이블에 Session 추가 * fix: 세션 삭제 시 출석 상태 조회에 비관적 락 추가 * [WTH-161] 마이그레이션 후 정리 (#16) * [WTH-160] user 도메인 리팩토링 (#18) * refactor: cardinal 도메인 분리 * refactor: cardinal 도메인 분리 * refactor: 유저 도메인 dto 개선 * refactor: 유저 관련 엔티티 개선 * refactor: 유스케이스 분리 * refactor: 소셜 로그인 개선 * refactor: 유스케이스 분리 * refactor: 테스트 변경사항 반영 * refactor: 경고 기능 제거 * feat: UserCardinal 쿼리 추가 * refactor: api 엔드포인트 제거 및 응답 코드 수정 * docs: CLAUDE.md 예외 코드 범위 현행화 * test: email 형식에 맞게 fixture 수정 * refactor: 트랜잭션 제거 * refactor: id private set 설정 * [WTH-150] board 도메인 리팩토링 (#20) * refactor: 미사용 enum 제거 * refactor: 어드민용 API에서는 전체 게시판이 조회되도록 수정 * refactor: 게시판 수정 API PATCH 관련 주석 및 테스트 추가 * refactor: 게시글 수정시 PATCH 규칙 개선 및 삭제된 게시판의 글인 경우 반환하지 않도록 설정 * refactor: 게시판 상세 정보 조회 API 어드민으로 이전 * refactor: 미사용 메서드 제거 * refactor: 미사용 메서드 제거 * refactor: 게시판 정보가 바뀌거나, 유저 권한이 바뀌는 케이스 대응 * docs: todo 주석 추가 * docs: todo 주석 설정 * refactor: private set 설정 * chore: lint 수정 * docs: 엔티티 구조 업데이트 * docs: 엔티티 구조 업데이트 * refactor: 엔티티 구조 개선 * refactor: 엔티티 구조 변경에 따른 테스트 수정 * docs: 주석 추가 * test: 불필요한 문구 제거 * [WTH-174] weeth server 클로드 코드 커스텀 업데이트 (#19) * docs: 응답코드 5자리로 개선 * docs: 응답코드 5자리로 개선 * chore: ktlint hook 추가 * docs: 클로드 코드 관련 컨텍스트 최신화 * docs: 리드미 업데이트 * [WTH-169] QR 출석체크 기능 구현 (#17) * refactor: 사용되지 않는 ScheduleTimeCheck 관련 코드 제거 * refactor: 출석 코드 자릿수 4자리에서 6자리로 변경 * feat: QR 출석 Redis 포트 및 어댑터 추가 * feat: QR 출석 예외 클래스 및 에러/응답 코드 추가 * feat: QR 토큰 응답 DTO 및 어드민 QR 생성 UseCase 추가 * feat: 출석 체크인 로직을 Redis QR 코드 기반으로 변경 * feat: 어드민 QR 코드 생성 엔드포인트 추가 * test: GenerateQrTokenUseCase 및 ManageAttendanceUseCase checkIn 테스트 추가 * style: 린트 적용 * refactor: ManageAttendanceUseCase 가독성 개선 * style: 개행 추가해서 가독성 개선 * refactor: Attendance status 필드 private set 적용 * style: 린트 적용 * fix: 출석 에러코드 번호 수정 * fix: 세션 에러코드 번호 수정 * refactor: 불필요한 @transactional 어노테이션 제거 * refactor: 난수 생성 방식을 SecureRandom으로 변경 * feat: 출석 체크 시간 검증 추가 * refactor: QR 출석 Redis 키를 code에서 sessionId로 변경 및 세션 ID API 추가 * docs: QrAttendancePort 메서드 주석 추가 * test: QR 출석 체크인 테스트 수정 및 시간 검증 케이스 추가 * style: 린트 적용 * refactor: TTL_SECONDS 중복 상수를 QrAttendancePort로 통합 * [WTH-176] club 도메인 엔티티 추가 작업 (#21) * test: 누락 작업 추가(게시판 조회 시 검증 추가) * feat: Club 도메인 entity, vo 구현 * feat: vo 구현 * feat: repository 구현 * feat: 예외 추가 * feat: enums 추가 * feat: ClubRepository 추가 * feat: ClubMember Test 추가 * feat: Club Test 추가 * chore: lint 설정 * feat: tsid 생성 유틸 추가 * docs: club 응답 코드 추가 * refactor: 동아리 이름은 중복 가능하도록 수정 * refactor: 학교 이름 + 동아리 이름 Unique 제약조건 추가 * test: 테스트 보강 * [WTH-181] club 도메인 추가 작업 2 (#22) * refactor: UUID 사용으로 인한 code 길이 수정 및 프로필, 배경 사진 추가 * feat: Club Code UUID 적용 * feat: ClubMember 관련 정책 추가 * feat: dto 추가 * feat: 동아리 조회 서비스 구현 * feat: 동아리 멤버 조회 서비스 구현 * feat: 동아리 관리 유스케이스 구현 * feat: 동아리 가입/탈퇴 유스케이스 구현 * feat: 동아리 멤버 관리 유스케이스 구현 * feat: 컨트롤러 추가 * refactor: 스프링 빈 등록 * feat: 응답 코드 추가 * refactor: 주석 추가, 예외 이름 변경 * refactor: 테스트 케이스 보강 * refactor: MVP 기획에 맞게 다중 동아리 지원 막기 * refactor: 기수 정보 및 추가 사용자 정보 반환 * feat: mapper 구현 * refactor: id 기반 검증 제거 * feat: TSID를 Long으로 디코딩하는 어노테이션 추가 * docs: 주석 추가 * chore: lint 설정 * refactor: 중복 에러 코드 제거 * refactor: ClubMember 관련 DTO 수정 * refactor: 전처리 추가 * refactor: 테스트 케이스 추가 * refactor: 동시 가입 오류가 터지지 않게 락 적용 * refactor: 동시 가입 오류가 터지지 않게 락 적용 * refactor: 유스케이스명 수정 * refactor: ClubMember 관리시 해당 동아리에 속했는지 검증 추가 * refactor: 동아리 업데이트시 PATCH 계약에 맞게 수정 * feat:프로필, 배경 사진 삭제 API 추가 * [WTH-183] club 도메인 추가 작업 3 (#23) * refactor: Account 도메인 클럽 추가 * refactor: Account 도메인 클럽 추가 * refactor: Attendance 도메인 사전 작업 * refactor: Board 도메인 Club 추가 작업 * refactor: Board 도메인 Club 추가 작업 * refactor: Cardinal 도메인 Club 추가 작업 * refactor: comment 관련 테스트 반영 * refactor: Penalty 도메인 Club 사전 작업 * refactor: Schedule 도메인 Club 추가 작업 * refactor: Session 도메인 Club 추가 작업 * chore: 컨벤션 수정 * refactor: 계좌 생성 메서드 수정 * refactor: 기존 메서드 성능 개선 * docs: clubId 관련 내용 추가 * test: 테스트 반영 * chore: lint 설정 * refactor: 리뷰 내용 반영 * docs: todo 주석 추가 * [WTH-180] 대시보드 api 구현 (#24) * feat: NoticeRead 엔티티 및 Repository 추가 * feat: 공지 읽음 처리 유스케이스 및 API 추가 * feat: 대시보드용 Reader 인터페이스 추가 * feat: 대시보드 DTO, Mapper, Exception 추가 * feat: 대시보드 QueryService 구현 * feat: 대시보드 Controller 구현 * test: 대시보드 및 공지 읽음 처리 테스트 추가 * docs: 대시보드 도메인 ID API 설계 문서 추가 * docs: Swagger 도메인 코드 범위에 Club, Dashboard 추가 * style: 불필요한 주석 제거 * style: 불필요한 import문 삭제 * refactor: 공지 읽음 ID 조회 시 기간 제한 추가 * refactor: EventRepository findByDateRange @query 제거 * refactor: 사용하지 않는 메서드 제거 * style: 작성자 프로필 이미지 미구현 TODO 주석 추가 * refactor: 미사용 메서드 제거 * fix: 공지 읽음 처리 동시 요청 시 중복 저장 예외 처리 * fix: PostRepository 쿼리 보조 정렬 키 추가 * refactor: 공지 읽음 처리 API에 boardId 범위 제한 추가 * refactor: myClubs 조회 시 활성 멤버십만 반환하도록 수정 * refactor: validateMembership에서 비활성 멤버 접근 차단 * refactor: clubId api 지침에 맞게 수정 * style: 린트 적용 * refactor: NoticeRead 방식을 LastNoticeRead 방식으로 수정 * refactor: clubInfo DTO 초대코드 필드 추가 * refactor: 스케줄 일정 enum으로 변경 * refactor: 최신공지 응답 구조 수정 * refactor: MVP 미지원 홈 응답 필드 임시 제외 * refactor: home 대시보드에 내활동 응답 추가 * refactor: 대시보드 게시글/공지 조회 시 해당 클럽 검증 및 클럽 범위 필터링 적용 * style: 린트 적용 * refactor: 공지 읽음 처리 club 검증 추가 * fix: 대시보드 공지 시간 필드를 정렬 기준인 createdAt으로 통일 * test: 공지 읽음 갱신 시 lastReadAt 변경 여부 단언 추가 * refactor: SessionReader에 findByDateRange API로 통일 * refactor: 불필요한 쿼리문 삭제 * [WTH-189] club 추가 마무리 작업 (#25) * refactor: Account 도메인 ClubId 추가 및 접근 제어 * refactor: Board 도메인 ClubId 추가 및 접근 제어 * refactor: Cardinal 도메인 ClubId 추가 및 접근 제어 * refactor: CLub 도메인 리팩토링 * refactor: CLub 도메인 리팩토링 * refactor: schedule 도메인 club 추가 및 접근 제어 * refactor: session 도메인 club 추가 및 접근 제어 * refactor: ManageCardinalUseCase.kt 접근제어 추가 * refactor: applyOb 메서드 ClubMember로 이전 * refactor: attendance 도메인 clubMember 추가 * refactor: apply-ob api 추가 * refactor: apply-ob dto 추가 * refactor: ClubMember 조회시 clubId 추가 * refactor: Penalty clubMember 추가 * refactor: 접근 제어 추가 * refactor: 기수 정책 추가 * refactor: 미사용 API 제거 * refactor: 미사용 코드 제거 * docs: 주석 추가 * refactor: response code 추가 * refactor: mock 제거 * refactor: lint 설정 * test: ClubMemberCardinalPolicyTest 추가 * refactor: 에러코드 수정 * docs: 주석 추가 * refactor: 코드 정리 * refactor: 테스트 오류 수정 * docs: TODO 주석 추가 * refactor: isCurrent 메서드명 변경 및 주석 추가 * refactor: 리뷰 내용 반영 * [WTH-197] 동아리 정보 공개 api 구현 (#27) * refactor: 동아리 정보 공개 API 구현 * refactor: @PathVariable 어노테이션 제거 * refactor: dto명 수정 * [WTH-198] 동아리 개설 가입시 기수 입력 및 초기화 (#28) * feat: 최초 기수 설정 dto 및 중복 설정 예외 추가 * feat: 락을 포함한 조회 및 검증 메서드 추가. (추후 WTH-200 이슈에서도 사용 예정) * feat: 락을 포함한 조회 및 검증 메서드 추가. * refactor: 동아리 개설시 기수 초기화 로직 추가 * feat: 기수 초기 설정시 출석 초기화 로직 추가 * refactor: 개설시 기수 입력 추가 * feat: 활동 기수 최초 설정 API 추가 * docs: 주석 추가 * refactor: PathVariable 제거 * refactor: n+1 제거 및 주석 추가 * refactor: 미사용 import 문 제거 * refactor: 가볍게 검증하도록 수정 * [WTH-203] 동아리 개설/가입 수 제한 (#30) * feat: 동아리 최대 가입 예외 추가 * refactor: 동아리 생성가입 제한 로직 추가 * refactor: 미사용 예외 삭제 * docs: 주석 최신화 * refactor: 충돌해결 * refactor: 동시성 문제 해소 * [WTH-210] 학교 학과 open api (#29) * feat: Redis 캐시 설정 추가 * feat: 커리어넷 API 설정 추가 * feat: 학교/학과 도메인 레이어 추가 * feat: 커리어넷 Port/어댑터 구현 * feat: 학교/학과 조회 API 엔드포인트 추가 * feat: 학교/학과 조회 API 공개 접근 허용 * test: 학교/학과 api 테스트 구현 * docs: university 도메인 코드 범위 추가 * style: 개행 추가 * refactor: 커리어넷 Port 구조 개선 * style: 린트 적용 * refactor: 코드 리뷰 반영 * test: 변경된 구조에 맞게 테스트 수정 * fix: Redis 캐시 Kotlin 역직렬화 오류 수정 * style: 린트 적용 * fix: Redis 캐시 역직렬화 타입 검증 범위 제한 * refactor: 응답이 한글부터 정렬되게 수정 * fix: university API 엔드포인트 경로 수정 * refactor: GetSchoolQueryService, GetMajorQueryService를 GetUniversityQueryService로 통합 * refactor: port 네이밍 변경 * test: QueryService 통합에 맞춰 테스트 수정 * test: 학교/학과 캐시 통합 테스트 및 성능 벤치마크 추가 * refactor: 코드 리뷰 반영 * refactor: TTL 7일로 수정 * fix: MySQL 버전 수정 * fix: Jackson 역직렬화 허용 타입을 ArrayList로 제한 * [WTH-208] 사용자 프로필 기능 추가 (#26) * feat: User 엔티티 term, profile 필드 추가 * feat: 약관 동의 api 구현 * feat: 프로필 이미지 업로드/수정 api 구현 * feat: OAuth 프로필 이미지 저장 * feat: 파일 타입에 USER PROFILE 추가 * feat: UserInfo 공통 DTO 추가 * refactor: 사용자 정보가 필요한 Response/Mapper에서 UserInfo로 통일 * feat: 프로필 bio 및 profileImageUrl 필드 추가 * test: 사용자 프로필 필드 변경에 따른 관련 테스트 수정 * style: 린트 적용 * feat: 일정 클럽 필터링 기능 추가 * refactor: 대시보드 내활동 userInfo 네이밍 변경 * style: UserMapper DTO 생성자 named arguments 적용 * fix: CommonResponse<Void> nullable 타입 수정 * refactor: agreeTerms 검증 및 할당 로직 개선 * refactor: User 엔티티 생성자 정리 및 lateinit 제거 * fix: UserInfo.role nullable 타입 제거 * fix: AgreeTermsRequest 검증 메시지 추가 * test: User 엔티티 agreeTerms, updateProfileImageUrl, bio 테스트 추가 * fix: bio 필드 최대 길이 30자로 변경 * fix: profileImageUrl 공백 입력 시 null 정규화 * fix: User 생성자 name, profileImageUrl 검증, 정규화 추가 * refactor: User 생성자에서 id 제거 및 관련 테스트 변경 * refactor: bio 필드를 User에서 ClubMember로 이전 * refactor: 동아리별 멤버 프사 추가 및 소셜 프사 제거 * refactor: 소셜 로그인 이름 미제공 시 이메일 앞자리로 대체 가입할 수 있게 수정 * refactor: ClubMember 프로필 이미지 검증 추가 및 대시보드에 동아리 프사 추가 * style: bio 기본값 제거 및 Swagger 예제 추가 * feat: 동아리 멤버 프로필 사진 삭제 API 추가 * test: 동아리 멤버 프로필 사진 삭제 UseCase 테스트 추가 * refactor: 동아리 활동 프로필 수정 API 통합 * refactor: 동아리 멤버 프로필 전체 가입 동아리에 일괄 적용 * docs: 주석 추가 * style: 린트 적용 * refactor: 코드 리뷰 반영 * feat: User 엔티티에 school 필드 추가 * refactor: 응답코드 번호에 맞게 수정 * refactor: 미사용 api 제거 * [WTH-201] 동아리 입력 출력값 검증 (#31) * feat: 주사용 연락처 정보 추가 * refactor: 주사용 연락처 추가 반영 작업 * refactor: Dto 검증 추가 및 미사용 import 문 제거 * refactor: 미사용 import 제거 및 주석 추가 * refactor: 전화번호 global vo로 전환 * refactor: 주사용 연락처 enum 추가 * refactor: 전화번호 예시 "-" 제거 * docs: 주석 추가 * refactor: 주 연락처가 이메일인데, 이메일이 없는 경우 커스텀 예외 처리 * [WTH-204] LEAD 권한 이양 API 구현 (#33) * feat: LEAD 권한 이양 API 구현 * feat: LEAD 권한 이양 관련 예외 추가 * refactor: 동시성 방어를 위한 락 추가 * docs: LEAD 관련 설명 추가 * test: 관련 테스트 추가 * docs: lint 설정 * refactor: 사전 검증 추가 * refactor: 커스텀 예외 검증으로 수정 * [WTH-196] 인증 정책 수정, 이미지 관련 필드 수정 (#32) * refactor: JWT Token에서 Role 제거 * refactor: CurrentUserRole 관련 파일 제거 * refactor: Board 도메인 Role 제거 및 LEAD 접근 제어 수정 * refactor: Board 도메인 Role 제거 * refactor: user.role, profileimage 제거 * refactor: 작성자 정보를 매핑하기 위한 ClubMember 조회 메서드 추가 * refactor: User.Role -> MemberRole로 전환 * refactor: UserInfo 반환을 위한 매핑 로직 추가 * test: 테스트 일괄 반영 * chore: ADMIN 경로 검증에서 일반 인증 경로로 수정 * chore: 대체 예정 Deprecated 처리 * refactor: storageKey를 저장하는 방식으로 수정 * refactor: storageKey를 저장하는 방식으로 수정 * test: 이미지 관련 테스트 일괄 반영 * refactor: 미사용 dto 제거 * refactor: dto 길이 제한 추가 * refactor: 헬퍼 메서드 추가 * refactor: 불필요 코드 제거 * refactor: 중복 검증 제거 * refactor: isAdmin 메서드 제거 * [WTH-200] 동아리 정책 수정 (#34) * refactor: ClubMember 정책 분리 * feat: 분리 정책 테스트 추가 * refactor: 분리 반영 * refactor: ClubCode 검증 로직 개선 * [WTH-202] 출석 정책 및 구조 정리 (#36) * refactor: Session, Attendance private set 적용 * refactor: 출석 자동 마감 스케줄러를 매일 00시로 변경 * refactor: 미사용 API 제거 * refactor: AttendanceSummar.code 예시 6자리로 수정 * refactor: 출석 초기화 트리거 정리 * refactor: 출석 체크인, 마감 락 추가 * refactor: 배치 락 및 데드락 방지 * refactor: 세션 생성 시 출석 쿼리 최적화 * test: 배치 락 변경과 관련된 테스트 수정 * feat: 출석 마감 예외 클래스 추가 * style: 린트 적용 * fix: updateStatus 빈 리스트 예외 방지 * fix: 주석 제거 * refactor: DB 레벨 잠금 순서 보장 * fix: 주석으로 정렬 명시 * refactor: TODO 주석추가 * [WTH-205] 내 동아리 활동 정보 조회 api 추가 (#35) * refactor: 컨트롤러 분리 * refactor: dto에 role, status 정보 추가 * refactor: 가입 동아리 여부, 반환 메서드 추가 * refactor: 조회 메서드 책임 이전 * refactor: 응답코드 수정 * refactor: 매퍼로 위임 * [WTH-206] 게시글 좋아요 api (#38) * feat: 게시글 좋아요 도메인 추가 * feat: 게시글 좋아요 DTO 추가 * feat: 게시글 좋아요 토글 유스케이스 추가 * feat: 게시글 좋아요 토글 API 추가 * style: 린트 적용 * test: 관련 테스트 수정 * refactor: 게시글 좋아요 플래그 방식으로 변경 * refactor: 게시글 좋아요 응답 코드 단일화 * refactor: 게시글 좋아요 동시성 스냅샷 해결 * refactor: 비지니스 로직 이동 * refactor: 불필요한 락 제거 * refactor: 락 타임아웃 응답 코드 변경 * test: 게시글 좋아요 테스트 추가 * refactor: 응답 mapper로 위임 * refactor: 좋아요 응답 PostLikeResponse로 통합 * [WTH-207] 게시판 기본 기능 수정 (#37) * feat: 게시글 작성 시 현재 기수 자동 반영 * feat: 전체 게시글 조회 API 추가 * feat: 게시판 순서 수정 기능 추가 * refactor: 게시판 순서 변경 시 중복 boardId 검증 추가 * feat: 게시판 생성 시 displayOrder 자동 설정 * refactor: 게시판 생성 시 reorder apply 블록 제거 * style: 린트 적용 * refactor: 게시판 이름 중복 확인 로직 추가 * test: 게시판 이름 중복 검증 테스트 추가 * feat: 동아리 생성 시 공지사항 게시판 자동 생성 * style: 린트 적용 * refactor: 공지사항 게시판 생성 시 순서 명시 * test: 게시판 재정렬 후 검증 테스트 추가 * feat: 게시판 정보 추가 * refactor: 게시판 관련 코드 정리 * style: 가독성 개선 * style: 린트 적용 * fix: board EntityGraph 누락 수정 * refactor: cardinal 조회 예외 방지 * docs: 게시판 순서 변경 요청 DTO 설명 추가 * refactor: 대시보드 최신글 권한별 필터링 적용 * refactor: 미사용 매서드 제거 및 테스트 보완 * refactor: 공지사항 순서 고정 및 가상 전체 게시판 추가 * test: 게시판 고정, 전체 관련 테스트 추가 * refactor: 어드민 게시판 postCount 및 가상 전체 게시판 추가 * refactor: 게시판 이름, 순서 변경 제한 및 삭제 시 displayOrder 재정렬 * fix: 미팅 테이블명 세션으로 수정 * refactor: 세션 조회 시 기수 검증 추가 * refactor: 전체 게시글 목록 조회 시 좋아요 추가 * refactor: 좋아요와 관련된 테스트 수정 * test: 기수 검증 테스트 추가 * [WTH-221] 액세스 토큰 리프레시 토큰 쿠키 반환 (#39) * feat: 쿠키 관련 환경변수 설정 * feat: 쿠키 유틸 추가 * refactor: 쿠키에서 토큰 추출 로직 추가 * refactor: 쿠키에서 토큰 추출 로직 추가 * refactor: 쿠키를 반환하고 받도록 인증 관련 API 수정 * refactor: 약관 동의 시 가입 승인 & 토큰 발급하도록 로직 수정 * refactor: 토큰 타입 추가 (임시, 액세스) * refactor: 인증 API 수정 * refactor: 가입 미완료시 예외처리 * test: 테스트 환경변수 추가 * feat: token type enum 추가 * feat: 동아리 활동 정보 API 추가 * refactor: public 겨로 저리 * feat: 유저 프로필 완성 여부 확인 API 추가 * [WTH-220] 동아리 이미지도 File을 타도록 수정 (#40) * refactor: 동아리 이미지도 File을 타도록 수정 * refactor: lint 설정 * feat: 추방 멤버 복구 API 추가 * feat: 추방 멤버 복구 API 추가 * refactor: 페널티 타입 삭제 * refactor: 경로 파라미터로 수정 * refactor: UUID 예시 수정 * refactor: 학교 + 학번으로 중복 검사하도록 수정 * refactor: 페널티 타입 제거 * refactor: 스웨거 자물쇠 제거 * refactor: 스웨거 자물쇠 제거 * docs: 주석 추가 * refactor: 생성시간을 반환하도록 수정 * HOTFIX: cors 주소 추가 * HOTFIX: PATCH 매핑으로 수정 * [WTH-228] 공지사항 삭제 가능 (#41) * fix: 공지사항 삭제 방어 로직 추가 * test: 공지 게시판 삭제 테스트 추가 * refactor: 게시글 생성 제한 추가 * fix: 게시글 작성 작성자의 현재 최신기수로 변경 * fix: countBy 반환 타입 Int → Long 수정 * test: ClubMemberCardinalTestFixture 추가 * style: 린트 적용 * refactor: 게시판 생성 동시성 이슈 해결 * fix: countBy 반환 타입 Int로 수정 * refactor: 공지 게시판 생성 방어 로직 * refactor: 모든 게시판으로 생성 개수 제한 * [WTH-229] 유저 기수 수정 (#43) * refactor: 기수 필드 연도, 학기 제거 * refactor: 현재 진행 기수 지정 API로 변경 * refactor: 기수 정책 동아리로 범위 제한 * feat: 기수 수정 api 추가 * style: 린트 적용 * refactor: 기수 연도, 학기 제거 * test: club id 불일치 수정 * refactor: 기수 출석 카운트 복구 로직 추가 * refactor: 출석 복구 최신기수로 제한 * refactor: 출석 통계 및 패널티 리셋 수정 * test: 기수 테스트 픽스처 수정 * refactor: 페널티 복구 로직 추가 * [WTH-226] 세션 반복 생성 (#42) * feat: 세션 반복 생성을 위한 entity, enums 추가 * feat: 세션 반복 생성을 위한 dto/예외 설정 * feat: 세션 그룹 저장소 및 세션 저장소 메서드 추가 * refactor: duration 기반으로 계산하도록 개선 * refactor: 세션 생성 유스케이스 분리 + 반복 생성 로직 추가 * refactor: 세션 업데이트 유스케이스 분리 + 반복 세션 수정 로직 추가 * feat: 반복 세션 삭제 유스케이스 추가 * refactor: 예외 처리시 data도 선택적으로 받을 수 있도록 수정 * refactor: 세션 관리 유스케이스 분리 * refactor: 세션그룹 삭제 메서드 추가 * refactor: 메퍼 메서드 추가 및 세션 도메인으로 이전 * feat: 세션 그룹으로 조회 메서드 추가 * refactor: 세션 그룹 별 조회하도록 로직 개선 * refactor: 세션 그룹 별 조회하도록 로직 개선 * refactor: 이번 주 세션 리스트로 반환하도록 수정 * refactor: duration 계산 메서드 분리 * test: 테스트 적용 * refactor: 이번 주 세션 리스트로 반환하도록 수정 * refactor: lint 설정 * feat: 세션 반복 관련 API 가 * refactor: UI에 맞게 API 수정 * refactor: 세션 도메인으로 이전 * refactor: 응답 DTO 개선 * refactor: PATCH 스럽도록 수정 * refactor: 세션 id로 매핑 수정 * refactor: import 문 수정 * refactor: 엣지케이스 관련 주석 보강 * refactor: 제약조건 추가 * refactor: 개별 삭제 동시성 문제 보강 * refactor: 개별 삭제 동시성 문제 보강 * refactor: totalCount를 저장하지 않고, 조회시 계산해 반환하도록 수정 * refactor: dto 설명 수정 * refactor: 세션 그룹 업데이트 및 설명 포맷 개선 * refactor: 락 조회시 정렬 추가 * refactor: 장소, 설명 선택 처리 * refactor: QueryHints 추가 * refactor: id 변경 * refactor: fixture 파라미터 변경 * [WTH-236] 랜딩 문의하기 api 구현 (#45) * chore: Notion, Slack 설정 추가 * feat: 문의 port 인터페이스 추가 * feet: 문의하기 로직 구현 * feet: Notion, Slack Adapter 구현 * feet: 문의하기 엔트포인트 추가 * refactor: 응답코드 수정 * refactor: 문의하기 비동기로 변경 * chore: AsyncConfig 추가 * [WTH-237] 애플 로그인 수정 및 로그인 시 유저 정보 반환 (#44) * fix: 애플 로그인 수정 * refactor: 로그인 시 유저 이름 반환 * refactor: 주석 및 파일명 변경 * refactor: 파싱 로직 이전 * refactor: 컨트롤러 정리 * test: 의존성 추가 * HOTFIX: CORS 경로 변경 * fix: 도메인 설정이 변경된 경우 바로 반영 되도록 로직 수정 (#46) * [WTH-251] post comment 작성자 타입을 club member로 변경 (#48) * refactor: Post/Comment user 필드 clubMember로 변경 * refactor: clubMember로 fetch join * refactor: userReader 제거 * refactor: Mapper authorMember 제거 * refactor: Service buildMemberMap 제거 * refactor: Post/Comment fixture user를 clubMember로 변경 * test: 관련 test 수정 * test: 관련 test 수정 * [WTH-247] 유저 필드 nullable 처리 (#47) * fix: 선택 필드 nullable 및 PATCH 전략에 맞게 수정 * refactor: 초기 유저 정보 설정시 프로필 완성 여부를 검증하도록 수정 * refactor: 파일 업데이트시 하드 딜리트 시켜 replace가 정상적으로 되도록 수정 * refactor: isNew 필드 추가 * refactor: 파일 목록 반환 * test: 테스트 반영 * refactor: 업데이트시 검증 보강 * refactor: 업데이트시 검증 보강 * refactor: 충돌해결 * HOTIFX: 랜딩 CORS 추가 * [WTH-252] 출석 요약 조회 api에 세션 id가 없어 출석이 불가능한 문제 (#49) * refactor: 세션 id 반환 * refactor: 경로 매개변수로 변경 * test: test 반영 * [WTH-260] 동아리 생성시 club id 동아리 이름 즉시 반환 (#50) * feat: 동아리중복 검증 추가 * feat: 동아리중복 검증 추가 * refactor: 동아리 생성 응답 반환 * refactor: 테스트 반영 * [WTH-278] 파일 수정 오류 해결 (#52) * fix: 삭제 즉시 적용 * test: test mockk 추가 * [WTH-309] 문의 내용 NotBlank 검증 제거 (#53) * refactor: 문의 내용 선택 입력으로 변경 * [WTH-282] 댓글 좋아요 백엔드 qa (#54) * refactor: 댓글 반환 시간 작성 시간으로 변경 * refactor: 좋아요 토글을 like/unlike로 분리 * refactor: 공통 검증 로직 메서드로 추출 * refactor: 응답 예시 추가 * [WTH-294] qr 만료시간 실시간 전달 (#51) * feat: SSE 연결 관리를 위한 SseEmitterStore 추가 * feat: SSE port 구현 * feat: SSE adapter 구현 * feat: QR 생성 시 SSE로 출석 가능한 시간 전송 * feat: 출석 SSE 구독 엔드포인트 추가 * test: 관련 테스트 수정 * test: SseEmitterStore 단위 테스트 추가 * refactor: SsePort를 Broadcast, Subscribe로 분리 * feat: SSE Subscribe UseCase 추가 * refactor: SseEmitterStore 동시성 처리 * test: 관련 테스트 추가 및 수정 * test: 동시성 테스트 무한 대기 방지 * test: 동시성 테스트 수정 * refactor: @synchronized 병목 해소 * refactor: event로 패키지 변경 * style: 린트 적용 * [WTH-310] 대시보드 최근 게시글에 좋아요 여부 필드 추가 (#55) * refactor: 대시보드 최근 게시글 응답에 isLiked 필드 추가 * test: isLiked 테스트 추가 * [WTH-105] Weeth Server 모니터링 설정 (#56) * feat: 프로메테우스 API Overview 추가 * feat: 프로메테우스 외부/내부 인프라 대시보드 * feat: 로그 MDC 설정 추가 * feat: 관측 설정 추가 * refactor: redisConnectionFactory 주입 방식으로 수정 * refactor: 도커 네트워크 접근 허용 * refactor: 로그백 설정 수정 * refactor: 환경 설정 * feat: 로컬 모니터링 완성 * feat: 로컬 모니터링 완성 * refactor: 도커 파일 Java Agent 설정 * refactor: 도커 컴포즈 Java Agent 설정 * feat: 개발 서버 모니터링 설정 추가 * feat: 개발 서버 도커 컴포즈 추가 * feat: 개발 서버 배포 스크립트 추가 * feat: 운영 서버 모니터링 설정 추가 * feat: 운영 서버 도커 컴포즈 추가 * feat: 운영 서버 배포 스크립트 추가 * feat: 개발/운영 깃액션 워크 플로우 추가 * context: 컨텍스트 업데이트 * refactor: 도커 컴포즈 오타 수정 * refactor: 리버스 프록시 엔드포인트 명시적 제한 * refactor: 로컬 환경변수 설정 추가 * refactor: 루트 경로 차단 추가 * refactor: 모니터링 환경변수 추가 * refactor: 디렉토리 오타 수정 * refactor: 환경변수 수정 * refactor: 로키 설정 수정 * refactor: 대기시간 증강 * HOTFIX: 어드민 유저 이름 환경 변수 설정 * HOTFIX: 모니터링 핫픽스 추가 (Log 수집 안됨, Lettuce 수집 안됨 해결) * HOTFIX: 빌드 오류 해소 * HOTFIX: 불필요 이미지 정리 코드 추가 * [WTH-327] 어드민 게시판 관리 수정 (#57) * refactor: 게시판 이름 중복 API 추가 및 설명 추가 * refactor: 기본 설정 게시판 설명 추가 * fix: DTO 추가 * [WTH-326] 게시판 관련 API에 boardId 필드 추가 (#58) * fix: 게시판 관련 응답에 게시판 ID 추가 * fix: 게시판 관련 응답에 게시판 ID 추가 * refactor: 게시판 관련 요청에 게시판 ID 추가 * test: 관련 테스트 수정 * refactor: 좋아요 응답 DTO를 PostLikeActionResponse로 분리, 게시글 수정 boardId 추가 * refactor: 게시판 글자수 20자로 제한 * [WTH-350] 게시판 권한 추가 (#60) * feat: 게시판 설정에 따른 권한 표시 * refactor: 댓글 허용에 따른 권한 확인 * [WTH-347] sse is empty 처리 (#59) * fix: SSE emitter 교체/삭제 시 메모리 누수 방지 * feat: SSE 구독 시 QR 상태 즉시 전송 추가 * feat: SSE 구독 시 QR 상태 즉시 전송 추가 * feat: QR 만료 시 SSE로 qr-close 이벤트 broadcast * refactor: 코드 리뷰 반영 * [WTH-349] 모니터링 인프라 개선 (#61) * [WTH-356] 리더 추방이 불가능하도록 수정 (#62) * refactor: 리더 추방이 불가능하도록 수정 * test: 테스트 수정 * [WTH] 오늘의 일정 단건 응답으로 수정 (#64) * refactor: 오늘의 일정 응답 1개면 바로 반환, 여러개면 현재 시간 이후에 가까운 1개로 반환하게 수정 * test: 관련 테스트 수정 * [WTH-358] 추방된 경우 동아리 리스트 미표기 (#65) * fix: 내 동아리 조회 시 활성화된 동아리만 조회 * refactor: mvp 후 추방된 상태까지 반환하도록 설정 * [WTH-361] 운영서버 모니터링 설정 (#66) * refactor: 운영 환경 템포 제거 * refactor: 운영 CORS 추가 --------- Co-authored-by: 전수현 <128474444+soo0711@users.noreply.github.com>
📌 Summary
커리어넷 Open API를 연동하여 학교 및 학과 정보를 조회하는 API를 구현합니다.
Redis 캐시를 적용해 외부 API 호출 비용을 최소화합니다.
📝 Changes
What
Why
How
@Cacheable로 커리어넷 API 결과를 Redis에 캐싱📸 Screenshots / Logs
전체 학과

💡 Reviewer 참고사항
CareerNetProperties는.env에서 API 키를 주입받으므로 로컬 실행 시 환경변수 설정 필요합니다.✅ Checklist
Summary by CodeRabbit
릴리스 노트
새로운 기능
테스트