728x90
반응형
개요
OpenRewrite는 소스 코드를 대규모로 리팩토링하기 위한 오픈소스 도구입니다. 특히 Java, SpringBoot 버전 업그레이드와 같은 복잡한 마이그레이션 작업을 자동화하는 데 매우 유용합니다.
1. OpenRewrite 소개
주요 특징
- 소스 코드의 LST(Lossless Semantic Trees) 기반 분석 및 변환
- 다양한 마이그레이션 레시피 제공
- 대규모 코드베이스에서 일관된 스타일로 코드 리팩토링 변경 지원
- 변경 사항에 대한 미리보기(dryRun) 및 검증 기능
사용 시나리오
- Java 버전 업그레이드 (e.g. 8 → 11 → 17 → 21)
- SpringBoot 버전 업그레이드 (e.g. 2.x → 3.x)
- 대대적인 보안 취약점 업데이트 (e.g. 2021년 log4j 보안취약점)
- 종속성 버전 업데이트
- 코드 스타일 및 패턴 업데이트
2. OpenRewrite의 예시
마이그레이션의 고통 포인트
SpringBoot 2.x에서 3.x로의 마이그레이션은 다음과 같은 귀찮은 작업들이 필요합니다
- javax.* → jakarta.* 패키지 변경 (수백 개의 import 문 수정)
- deprecated API들의 대체 작업
- Spring Security 설정 방식 변경
- 수많은 라이브러리 버전 호환성 체크
- Java 17 마이그레이션
// 이런 코드가 프로젝트에 수백 개...
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
// 모두 아래와 같이 변경 필요
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
3. OpenRewrite의 기술적 해결책
1. LST(Lossless Semantic Trees) 기반 분석
LST의 타입 속성(Type Attribution)
// 원본 코드
List<String> names = getNames();
names.stream()
.filter(name -> name.length() > 5)
.collect(Collectors.toList());
// LST는 다음과 같은 타입 정보를 보존
List<String> names = getNames(); // names: java.util.List<java.lang.String>
names.stream() // stream(): java.util.stream.Stream<java.lang.String>
.filter(name -> name.length() > 5) // filter(): Stream<T>
.collect(Collectors.toList()); // collect(): java.util.List<java.lang.String>
2. 포맷 보존(Format Preservation)
주석과 공백 보존
/* 원본 코드의 복잡한 포맷팅 */
public class User {
private final String name; // 사용자 이름
// 생성자 주석
public User(String name) {
this.name = name; // 필드 초기화
} // 후행 주석
}
// LST는 위의 모든 주석, 공백, 들여쓰기를 그대로 보존
// 기존 코드의 스타일
public class Example {
private void method1() {
if (condition) {
doSomething();
}
}
// OpenRewrite가 새로 추가하는 코드는 주변 스타일을 따름
private void newMethod() { // 동일한 들여쓰기 적용
if (condition) { // 중괄호 스타일 매칭
doSomethingNew();
}
}
}
2. 정교한 의존성 분석
- 프로젝트의 모든 pom.xml/build.gradle 파일 스캔
- 호환되지 않는 라이브러리 버전 자동 감지
- 전이 의존성 충돌 해결
4. 실제 사용 예시
1. Gradle 프로젝트 설정
plugins {
id("org.openrewrite.rewrite") version("5.x.x")
}
rewrite {
activeRecipe("org.openrewrite.java.spring.boot3.SpringBoot3Migration")
}
2. 마이그레이션 실행
# Gradle의 경우
./gradlew rewriteDryRun # 변경 예정 사항 미리보기
./gradlew rewriteRun # 실제 변경 적용
Junit Assert 리팩토링
// 변경 전
import org.junit.Assert;
Assert.assertTrue(condition);
// 변경 후
import static org.junit.Assert.assertTrue;
assertTrue(condition);
Spring Security 설정 마이그레이션
// 변경 전 WebSecurityConfigurerAdapter 사용
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
// 변경 후 컴포넌트 기반 설정
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth ->
auth.requestMatchers("/api/**").authenticated()
);
return http.build();
}
}
JPA 엔티티 마이그레이션
// 변경 전
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class User {
@Id
private Long id;
}
// 변경 후
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
private Long id;
}
5. 마이그레이션 전략
- 코어 모듈부터 시작
- 테스트가 많은 모듈 우선 적용
- 의존성이 적은 모듈부터 진행
# 변경 사항 미리보기
./gradlew rewriteDryRun
# 실제 변경 적용
./gradlew rewriteRun
# 특정 모듈만 적용
./gradlew :core:rewriteRun
6. 레퍼런스
728x90
반응형