Skip to content

Comments

refactor: Global, 도메인, QueryDSL 레포지토리 전반 Kotlin 마이그레이션(#131)#133

Open
chaiminwoo0223 wants to merge 2 commits intodevelopfrom
refactor/131
Open

refactor: Global, 도메인, QueryDSL 레포지토리 전반 Kotlin 마이그레이션(#131)#133
chaiminwoo0223 wants to merge 2 commits intodevelopfrom
refactor/131

Conversation

@chaiminwoo0223
Copy link
Contributor

📌 작업 내용 및 특이사항

✅ 변경 범위가 커진 이유

  • 이번 PR은 변경 파일 수가 383개로, 전체 diff를 모두 라인 단위로 확인하기 어려울 수 있습니다.
  • 이번 변경의 핵심은 Java + Spring, JPA → Kotlin + Spring, JPA 전환 과정에서 발생한 구조적 차이 보정입니다.
  • 특히, Kotlin 전환 과정에서 엔티티 ID(nullable) 처리 방식 차이로 인해 도메인, Repository, 테스트 코드 전반에 걸쳐 수정이 발생했습니다.
  • 삭제된 Java 코드보다는, Kotlin 전환 과정에서 추가/변경된 핵심 로직 위주로 리뷰해주시면 감사하겠습니다.

✅ Java → Kotlin 전환의 핵심 변화 (ID 처리 방식 차이)

  • Java + JPA 환경에서는 Long id를 전제로 사용하는 코드가 다수 존재했습니다.
  • Kotlin + JPA 환경에서는 var id: Long? = null 구조를 사용하게 되면서, 엔티티 ID nullable 처리 방식의 명시적 관리가 필요해졌습니다.
  • 이를 해결하기 위해 EntityExtensions.requireId() 확장 함수를 도입하여, 엔티티 ID를 사용하는 지점에서 명시적으로 null 여부를 검증하도록 개선했습니다.
  • 이로 인해 Application 계층의 DTO, Facade 및 테스트 코드 전반에 걸쳐 수정이 발생했습니다.

✅ ErrorCode 구현체에서 get 메서드 제거 → 프로퍼티 override 방식으로 리팩토링

  • 기존 Java 기반 ErrorCode 구현체는 getCode(), getMessage()와 같은 getter 메서드 중심 구조를 사용하고 있었습니다.
  • Kotlin 전환 과정에서 인터페이스에 정의된 프로퍼티를 override val 방식으로 직접 구현하도록 구조를 변경했습니다.
  • 이를 통해 불필요한 getter 메서드를 제거하고, Kotlin의 프로퍼티 중심 설계에 맞게 코드 구조를 단순화했습니다.
  • 하위 도메인(Member, Auth, Image 등)의 ErrorCode 구현체에서도 동일한 기준을 적용하여, get 메서드를 모두 제거하고 일관된 구조로 변경했습니다.
  • 결과적으로, ErrorCode 계층 전반의 중복 코드를 줄이고, Kotlin 표준 스타일에 맞는 명확한 표현 구조로 개선했습니다.

✅ Kotlin 마이그레이션 완료에 따른 @JvmStatic 정리

  • Kotlin 마이그레이션이 완료됨에 따라 불필요하게 적용되어 있던 @JvmStatic을 전반적으로 제거했습니다.
  • 초기 마이그레이션 단계에서 Java static 호출과의 호환을 위해 임시로 적용했던 설정입니다.
  • 현재는 Kotlin 중심 구조로 정리되어 대부분의 클래스에서 static 노출이 필요하지 않습니다.
  • 단, SpEL 기반 캐시 키 생성 로직에서 static 호출이 요구되는 CacheKeyFactory에서는 @JvmStatic을 유지했습니다.

✅ QueryDSL 및 도메인 Kotlin 마이그레이션 단계적 수행

  • 먼저 QueryDSL Repository 인터페이스와 도메인 모델을 Kotlin으로 마이그레이션했습니다.
  • 이 단계에서는 기존 Java 기준으로 생성된 Q 클래스가 여전히 존재하는 상태에서 점진적으로 전환을 진행했습니다.
  • 이후, QueryDSL Repository Adapter에서 Lombok 기반 어노테이션(@RequiredArgsConstructor)을 제거하고, 명시적 생성자를 추가하는 방식으로 구조를 변경했습니다.
  • 모든 마이그레이션이 완료된 이후 클린 빌드를 수행하여 Q 클래스를 Kotlin 기준으로 재생성했습니다.
  • 최종적으로 Java 기반 Q 클래스 의존성을 제거하고, Kotlin + QueryDSL(kapt) 환경으로 완전히 전환했습니다.

✅ 테스트 구조 개선 및 ID Nullable 대응 반영

  • 모든 Fixture를 불변 생성자 기반 구조로 전환하여, 테스트 객체 생성 시점의 상태가 변경되지 않도록 개선했습니다.
  • 기존 가변 필드 기반 Fixture에서 발생할 수 있는 테스트 간 상태 공유 및 사이드 이펙트 문제를 제거했습니다.
  • Kotlin 마이그레이션에 맞춰 DSL(withXXX) 패턴을 유지하면서도, 생성자 중심 구조로 책임을 명확히 분리했습니다.
  • Java → Kotlin 전환 과정에서 발생한 엔티티 ID nullable 구조를 테스트 코드에 반영했습니다.
  • requireId() 확장 함수를 기준으로, 저장 이후 ID 사용 시점이 명확히 드러나도록 테스트를 정비했습니다.

✅ 애플리케이션 진입점 및 테스트 구성 정비

  • StudytripApplication을 Java에서 Kotlin으로 전환했습니다.
  • StudytripApplicationTests를 Kotlin 기준으로 재작성했습니다.
  • Kotlin + Spring Boot 설정에 맞도록 애플리케이션 초기화 구조를 정리했습니다.
  • 클린 빌드 및 전체 테스트 실행을 통해 정상 동작을 검증했습니다.

✅ 공통 유틸 및 전역 지원 모듈 Kotlin 마이그레이션

  • DateUtil, FilenameUtil, OriginArgumentUtil Kotlin 전환
  • CacheNameConstants, CacheKeyConstants, CookieConstants, UrlConstants, BatchConstants Kotlin 전환
  • CacheKeyFactory Kotlin 전환
  • StandardResponse Kotlin 전환

✅ 전역 예외 처리 Kotlin 마이그레이션

  • CustomException Kotlin 전환
  • ErrorCode, CommonErrorCode Kotlin 전환
  • ErrorResponse, FieldErrorResponse Kotlin 전환
  • GlobalExceptionHandler, ValidationExceptionHandler Kotlin 전환

✅ Security 및 필터 Kotlin 마이그레이션

  • SecurityResponseHandler, CustomAccessDeniedHandler Kotlin 전환
  • CustomAuthenticationEntryPoint Kotlin 전환
  • OriginExtractionFilter, JwtAuthenticationFilter Kotlin 전환

✅ Config 구성 Kotlin 마이그레이션

  • WebSecurityConfig, WebClientConfig, SchedulerConfig, SwaggerConfig Kotlin 전환
  • RedisConfig, RedisCacheConfig Kotlin 전환
  • JpaAuditingConfig, QuerydslConfig Kotlin 전환
  • S3Config, TikaConfig, CdnConfig Kotlin 전환
  • BatchJobConfig, BatchStepConfig Kotlin 전환

✅ Batch 및 Cleanup 구조 Kotlin 마이그레이션

  • BatchJobScheduler, HardDeleteTasklet Kotlin 전환
  • HardDeleteExecutor, HardDeleteFacade Kotlin 전환

✅ 프로터티 Kotlin 마이그레이션

  • TokenProperties, KakaoOauthProperties Kotlin 전환
  • RedisProperties, SwaggerProperties Kotlin 전환
  • S3Properties, CdnProperties Kotlin 전환

🌱 관련 이슈


🔍 참고사항(선택)

  • build.gadle에 commons-lang3 3.20.0 추가 (전이 의존성 CVE 대응)
  • build.gradle에서 Lombok 의존성 및 설정 제거 (코틀린 정책)
  • build.gradle에서 Java 설정 제거 → Kotlin + QueryDSL(kapt) 기반으로 빌드 구성 전환

📚 기타(선택)

  • main java 패키지 삭제 (Kotlin 마이그레이션 완료)
  • test java 패키지 삭제 (Kotlin 마이그레이션 완료)

* feat: DateUtil.kt, FilenameUtil.kt, OriginArgumentUtil.kt 구현
* feat: HardDeleteTasklet.kt, BatchJobScheduler.kt 구현

* feat: CustomException.kt 구현
* feat: ErrorCode.kt, CommonErrorCode.kt 구현
* feat: ErrorResponse.kt, FieldErrorResponse.kt 구현
* feat: GlobalExceptionHandler.kt, ValidationExceptionHandler.kt 구현

* feat: CacheNameConstants.kt, CacheKeyConstants.kt, CookieConstants.kt, UrlConstants.kt, BatchConstants.kt 구현
* feat: CacheKeyFactory.kt 구현
* feat: StandardResponse.kt 구현

* feat: OriginExtractionFilter.kt, JwtAuthenticationFilter.kt 구현
* feat: SecurityResponseHandler.kt, CustomAccessDeniedHandler.kt 구현
* feat: CustomAuthenticationEntryPoint.kt 구현

* feat: WebSecurityConfig.kt, WebClientConfig.kt, SchedulerConfig.kt, RedisConfig.kt, RedisCacheConfig.kt, SwaggerConfig.kt 구현
* feat: JpaAuditingConfig.kt, QuerydslConfig.kt, S3Config.kt, TikaConfig.kt, CdnConfig.kt 구현
* feat: BatchJobScheduler.kt, BatchStepConfig.kt 구현

* feat: PomodoroQueryRepository.kt, PomodoroCommandRepository.kt 구현
* feat: StudyLogDailyMissionQueryRepository.kt, StudyLogDailyMissionCommandRepository.kt 구현
* feat: MissionQueryRepository.kt, MissionCommandRepository.kt 구현
* feat: DailyMissionQueryRepository.kt, DailyMissionCommandRepository.kt 구현
* feat: StampQueryRepository.kt, StampCommandRepository.kt 구현
* feat: TripQueryRepository.kt, TripCommandRepository.kt, DailyGoalCommandRepository.kt 구현
* feat: TripReportQueryRepository.kt, TripReportCommandRepository.kt, TripReportStudyLogCommandRepository.kt 구현
* feat: MemberQueryRepository.kt, MemberCommandRepository.kt 구현

* feat: TokenProperties.kt, KakaoOauthProperties.kt, RedisProperties.kt, SwaggerProperties.kt 프로퍼티 추가
* feat: S3Properties.kt, CdnProperties.kt 프로퍼티 추가

* feat: BaseTimeEntity.kt 구현
* feat: Pomodoro.kt, StudyLog.kt, StudyLogDailyMission.kt 구현
* feat: Mission.kt, DailyMission.kt, Stamp.kt 구현
* feat: Trip.kt, TripCategory.kt, DailyGoal.kt 구현
* feat: TripReport.kt, TripReportStudyLog.kt 구현
* feat: Member.kt, MemberCategory.kt, MemberRole.kt, SocialProvider.kt 구현

* feat: EntityExtentions 유틸 클래스를 구현하여, 엔티티 ID nullable 처리를 위한 requireId 확장 함수 추가

* feat: PomodoroQueryRepositoryAdapter.kt, PomodoroCommandRepositoryAdapter.kt 구현
* feat: MemberQueryRepositoryAdapter.kt, MemberCommandRepositoryAdapter.kt 구현
* feat: StudyLogQueryRepositoryAdapter.kt, StudyLogCommandRepositoryAdapter.kt 구현
* feat: StudyLogDailyMissionQueryRepositoryAdapter.kt, StudyLogDailyMissionCommandRepositoryAdapter.kt 구현
* feat: MissionQueryRepositoryAdapter.kt, MissionCommandRepositoryAdapter.kt, DailyMissionQueryRepositoryAdapter.kt, DailyMissionCommandRepositoryAdapter.kt 구현
* feat: StampQueryRepositoryAdapter.kt, StampCommandRepositoryAdapter.kt 구현
* feat: TripQueryRepositoryAdapter.kt, TripCommandRepositoryAdapter.kt, DailyGoalCommandRepositoryAdapter.kt 구현
* feat: TripReportQueryRepositoryAdapter.kt, TripReportCommandRepositoryAdapter.kt, TripReportStudyLogCommandRepositoryAdapter.kt 구현
* feat: MemberQueryRepositoryAdapter.kt, MemberCommandRepositoryAdapter.kt 구현

* refactor: ErrorCode 구현체를 프로퍼티 override 방식으로 변경하고 get 메서드 제거
* refactor: Java 설정 제거 및 Kotlin + QueryDSL(kapt) 기반으로 빌드 구성 전환

* test: PomodoroFixture.kt를 불변 생성자 기반 구조로 변경
* test: StudyLogFixture.kt를 불변 생성자 기반 구조로 변경
* test: MissionFixture.kt 불변 생성자 기반 구조로 변경
* test: StampFixture.kt 불변 생성자 기반 구조로 변경
* test: TripFixture.kt, DailyGoalFixture.kt, TripReportFixture.kt 불변 생성자 기반 구조로 변경

* test: CreatePomodoroRequestFixture.kt를 불변 생성자 기반 구조로 변경
* test: ConfirmStudyLogImageRequestFixture.kt, CreateStudyLogRequestFixture.kt, PresignStudyLogImageRequestFixture.kt를 불변 생성자 기반 구조로 변경
* test: CreateMissionRequestFixture.kt, UpdateMissionRequestFixture.kt 불변 생성자 기반 구조로 변경
* test: CreateStampRequestFixture.kt, UpdateStampRequestFixture.kt, UpdateStampOrderRequestFixture.kt 불변 생성자 기반 구조로 변경
* test: CreateTripRequestFixture.kt, CreateDailyGoalRequestFixture.kt, CreateTripReportRequestFixture.kt 불변 생성자 기반 구조로 변경
* test: UpdateTripRequestFixture.kt, UpdateDailyGoalRequestFixture.kt 불변 생성자 기반 구조로 변경
* test: PresignTripReportImageRequestFixture.kt, ConfirmTripReportImageRequestFixture.kt 불변 생성자 기반 구조로 변경

* chore: StudytripApplication.kt, StudytripApplicationTests.kt 추가
* chore: main java 패키지 삭제 (Kotlin 마이그레이션 완료)
* chore: test java 패키지 삭제 (Kotlin 마이그레이션 완료)
* chore: build.gadle에 commons-lang3 3.20.0 추가 (전이 의존성 CVE 대응)
* chore: build.gradle에서 Lombok 의존성 및 설정 제거

* fix: SpEL 호출 오류 해결을 위해 CacheKeyFactory 메서드에 @JvmStatic 추가
@chaiminwoo0223 chaiminwoo0223 self-assigned this Feb 22, 2026
@chaiminwoo0223 chaiminwoo0223 added the 🪄refactor 기능 개선 및 리팩토링 label Feb 22, 2026
Copy link
Contributor

@hisonghy hisonghy left a comment

Choose a reason for hiding this comment

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

고생 많으셨습니다!
몇 가지 댓글 남겨두었습니다!

open class StudyLog protected constructor(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
var member: Member,
Copy link
Contributor

Choose a reason for hiding this comment

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

엔티티에서 모든 필드를 var로 선언한 이유가 궁금합니다!

Copy link
Contributor Author

@chaiminwoo0223 chaiminwoo0223 Feb 23, 2026

Choose a reason for hiding this comment

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

JPA의 필드 기반 접근과 Dirty Checking 특성상, 영속 상태에서 값 변경이 가능해야 하므로 var로 선언했습니다. 외부에서 임의로 수정하지 못하도록 생성자는 protected로 제한하고, 상태 변경은 도메인 메서드를 통해서만 이루어지도록 설계했습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

JPA 특성을 고려하면 상태 변경이 가능하도록 var로 선언하는게 맞는데,
val를 통해서 불변 객체를 지향하는 코틀린 철학을 생각했을 때는 차이가 존재하는군요!
JPA와 코틀린은 지향하는 방식에서 차이가 있어서 잘 어울리지 않는다는 얘기가 있어서요,,

Copy link
Contributor Author

@chaiminwoo0223 chaiminwoo0223 Feb 24, 2026

Choose a reason for hiding this comment

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

@hisonghy JPA는 가변 모델을 전제로 하고, Kotlin은 불변을 지향하는 언어라 철학적인 차이가 있습니다. 엔티티는 가변(var)으로 두되, 변경은 도메인 메서드를 통해서만 이루어지도록 제한해 JPA의 동작 원칙과 Kotlin의 설계 철학을 함께 고려했습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🪄refactor 기능 개선 및 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🪄[REFACTOR]: Global, 도메인, QueryDSL 레포지토리 전반 Kotlin 마이그레이션

2 participants