[Diary] 신입 개발자의 오픈소스 컨트리뷰트 해보기 - Spring Data JPA
본문 바로가기

Development/Diary

[Diary] 신입 개발자의 오픈소스 컨트리뷰트 해보기 - Spring Data JPA

 
Spring Data Jpa에 저의 이름이..!

이번 글에서는 제가 Spring Data JPA에 기여하기 위해 겪은 과정을 소개합니다.

Issue 찾기

어느 오픈 소스던, Issue는 있기 마련입니다. 완벽한 소프트웨어는 없으니까요!

우선 기여할 오픈 소스 레포지토리를 찾아봅시다.

어떤 오픈 소스에 Issue를 선정할 것 인지는 우선 본인이 자주 쓰는 오픈 소스이며, 익숙한 프로그래밍 언어를 사용할수록 이슈를 해결하기 좋을 것입니다.

 

저는 Spring Data JPA에서 찾아보았습니다.

Spring Data JPA는 Spring 프레임워크에서 JPA(Java Persistence API) 기반의 데이터 접근을 더 간단하고 효율적으로 구현하기 위한 라이브러리입니다. ORM(Object Relational Mapping)을 지원하며, 데이터베이스와의 상호작용을 최소한의 코드로 처리할 수 있도록 다양한 기능을 제공합니다.

2024.11.24 기준 Spring Data JPA repo에 Issue들입니다.

Issue들은 여러 유형이 있습니다. 

  • 버그 제보
  • 개선해야 하는 작업
  • 질문

제목만 봤을 때, 간단해 보이는 Issue를 골라봅시다.

제가 선택한 Issue는 아래 내용입니다.

위 Issue 내용은 이렇습니다.

  • Spring Data JPA는 기존에 foo = ? 조건에서 쿼리 메서드의 인자가 null일 경우 이를 foo IS NULL로 자동 변환하는 기능을 제공했음 (#541에서 도입).
  • 하지만, 그 반대 조건인 foo <> ? (not equals)에서는 동일한 논리가 적용되지 않음.
  • 예를 들어, 메서드 인자가 null인 경우에도 foo <> ?로 남아 있음.
  • 결과적으로, foo IS NOT NULL로 변환되지 않아 일관성이 부족.

기존 코드를 개선해야 하는 내용 같습니다.

이 Issue는 #541에서 해결한 코드가 있을 테니, 그 부분을 참고해서 해결하면 될 것 같다는 생각이 들었습니다.

Issue와 관련된 클래스 찾기

직접 클래스를 까보는 것도 좋지만, 사이즈가 매우 크기 때문에 저는 우선 GPT에게 물어봤습니다.

그리고 이전에 수정했다고 한 #541 Issue를 들어가 봅니다.

위 commits를 들어가 봅시다.

GPT가 언급한 JpaQueryCreator 클래스에 위와 같은 코드 수정이 있었습니다.

 

그런데 자세히 보니, SIMPLE_PROPERTY 부분에는 isIsNullParameter()의 참, 거짓 여부에 따라 isNull()을 하거나, builder.equal()이란 걸로 값을 return 하고 있습니다.

아마 Issue 내용에서 "Spring Data JPA는 기존에 foo = ?조건에서 쿼리 메서드의 인자가 null일 경우 이를 foo IS NULL로 자동 변환하는 기능을 제공했음 (#541에서 도입)"를 처리해 준 것 같습니다.

 

그런데, case NEGATING_SIMPLE_PROPERTY에서는 이러한 처리가 되어있지 않습니다. 

따라서 NEGATING_SIMPLE_PROPERTY should use IS NOT NULL when argument is null이라는 Issue가 나온 것입니다.

고쳐야 할 코드를 찾은 것 같습니다!

해당 오픈소스의 Github Contribute 문서 참고하기

규모가 큰 오픈소스의 경우, 대게 본인들의 오픈소스에 기여하기 위해서 지켜야 하는 가이드라인을 제시합니다.

Spring data jpa의 경우, 아래와 같이 명시하고 있습니다.

https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc

 

spring-data-build/CONTRIBUTING.adoc at main · spring-projects/spring-data-build

Modules to centralize common resources and configuration for Spring Data Maven builds. - spring-projects/spring-data-build

github.com

주요 내용은 다음과 같습니다.

 

1. CLA 서명 필수: CLA(Contributor License Agreement)를 통해 기여자가 법적 동의를 해야 합니다.

2. 커밋 메시지 규칙: 첫 줄은 요약(80자 이내), 다음 줄에 상세 설명과 Issue 번호 포함(예: Closes #123).

3. 코드 포매터 적용: PR 전 코드 스타일을 맞춰야 합니다.

4. 본인 이름 추가 가능: 수정한 파일에 자신의 이름을 기록할 수도 있습니다.

 

내용 숙지 후 다음 단계로 넘어가 봅시다.

문서 양이 많다고 너무 부담 안 가져도 될 것 같습니다. 편한 방법은 다른 PR은 어떻게 썼는지 보고 따라 하는 것입니다. 잘 안 준수해도 대부분 받아줬더라고요.

Fork 후 코드 수정하기

spring-data-jpa를 본인 repository에 clone 후 branch를 만듭니다.

저는 issue/이슈번호로 브랜치를 만들었습니다. 

 

그리고 코드를 아래와 같이 수정했습니다.

case NEGATING_SIMPLE_PROPERTY:
    ParameterMetadata<Object> negatedExpression = provider.next(part);
    Expression<Object> negatedPath = getTypedPath(root, part);
    return negatedExpression.isIsNullParameter() ? negatedPath.isNotNull()
        : builder.notEqual(upperIfIgnoreCase(negatedPath), upperIfIgnoreCase(negatedExpression.getExpression()));

사실 case SIMPLE_PROPERTY: 에서 사용한 로직을 그대로 사용했습니다.

단, null 파라미터면 isNotNull()을 사용하도록 했습니다.

대략 추측한 동작은 이렇습니다.

 

ParameterMetadata <Object> negatedExpression = provider.next(part);

ParameterMetadata negatedExpression에는 아래와 같은 정보가 포함되어 있습니다.

이 클래스에는 값의 타입 (type)이 null 인지를 판별하는 isIsNullParameter() 메서드가 구현되어 있고, ParameterMetadataProvider(provider)에 의해 제공됩니다. 

 

Expression <Object> negatedPath = getTypedPath(root, part); 

Root <?> root는 쿼리의 root 객체를 나타냅니다. 예를 들어, SELECT e FROM Employee e에서 eroot에 해당합니다.

Part part는 쿼리 메서드에서 사용하는 필드를 나타내는 메타데이터입니다. 예를 들어, JPA의 쿼리 메서드 findByAgeNot의 경우 part.getProperty()"age"라는 필드명을 반환합니다.

 

위 두 객체를 조합하여, ParameterMetadata의 type이 null이면 Expression의 root, part를 기준으로 IS NOT NULL을 쿼리를 생성합니다.

PR 작성하기

PR을 작성하려 하니, 템플릿이 있었습니다. PR 전에 확인해야 하는 내용들 같습니다. 

그런데 test cases를 제출했냐는 문항이 있는데.. JpaQueryCreator를 테스트하는 클래스가 없어서 저는 쓰지 않았습니다...

 

아무튼 PR을 만들었다면 며칠 기다립니다. 저는 5일 정도 걸렸습니다. 

PR 왜 안 받아주지.. 하고 걱정하면 안 됩니다. 원래 늦더라고요 ㅎㅎ..

기여 성공!

보통은 merge를 하겠지만, Spring Data Jpa는 PR을 Close 하고 관리자 분들이 PR을 반영하는 식으로 운영하시더라고요.

한 번 반영된 코드를 볼까요? 

PR이 반영될 때는 관리자분들이 코드를 더 깔끔하게 리팩터링 하는 경우가 많습니다. 제가 작성한 코드는 아래와 같이 개선되었습니다.

  • 중복 제거: SIMPLE_PROPERTY와 NEGATING_SIMPLE_PROPERTY를 fall-through 스타일로 묶어 중복 로직을 줄였습니다.
  • 조건문 정리: 공통 로직을 조건문으로 나눠 더 명확하게 처리했습니다.

솔직히 조금 부끄럽지만, 좋은 학습 경험이 되었습니다.

마치며

오픈소스 기여는 PR을 제출하고 merge를 받는 것뿐만 아니라, 버그 제보개선 제안도 포함됩니다.

오픈소스 기여는 생각보다 어렵지 않으니, 여러분도 한 번 도전해 보시길 추천드립니다!