오픈소스 기여하기 spring-cloud/openfeign

이재훈·2026년 1월 16일
post-thumbnail

2026년에 목표 중 하나인 Spring 관련 오픈소스에 기여하기에 성공하였습니다! 🎉🎉🎉🎉

Spring Team과 소통하면서 많은 사람들이 사용하는 라이브러리에 제가 짠 코드가 들어갈 수 있다는게 신기하기도 하고 기분도 좋았습니다. 👍👍👍 그 과정을 잊기 전에 글로 작성해보려고 합니다.

0. 인트로

저는 오픈소스에 항상 기여하고 싶고 싶은 마음이 있었습니다. 그러나 막연하게 도전하는 것에 두려움이 있었던 것 같습니다. 2026년이 되어 개발자로써의 목표를 세웠는데 그 중 하나가 오픈소스 기여 였습니다.

링크드인에서 게시글들을 구경하던 와중 링크드인 - 오픈소스 기여모임 게시글을 보게 되었습니다.

오픈소스에 기여하는 첫걸음에 도움을 받을 수 있을 것 같아
"오픈소스 기여모임 10기"에 참여하게 되었습니다.

해당 과정은 Medium - AI 활용 오픈소스 기여 가이드 — 오픈소스 기여의 진입 장벽을 낮추고 판도를 바꿉니다! 를 참고하여 진행되었습니다.

1. 오픈소스 선정

Java/Spring Boot를 실무에서 사용하기 때문에 먼저 spring-projects에 어떤 라이브러리들이 있는지 확인을 해보았습니다.

평소에 작업하는 프로젝트들의 build.gradle을 보며 어떤 라이브러리들이 있는지 찬찬히 살펴보았습니다. 그러다보니 몰랐던 라이브러리들도 알 수 있게 되었다는게 좋았습니다.

특히나 Spring Session은 세션 상태를 서버에서 분리해 분산 환경에서도 일관된 세션을 제공하기 위해 쓰는 라이브러리로, 추후 회사에 도입해볼 수 있겠다는 생각이 들었습니다.

이슈를 선정할 때 저는 퍼블릭시티 코멧을 활용하였습니다. 코멧은 ai가 탑재된 브라우저로 프롬프트를 작성하면 직접 url에 접근이 가능하고 화면을 읽고 직접 처리까지 가능합니다.

이슈 번호를 추천 받고, 직접 이슈를 들어가서 처리 가능한 상태인지 확인을 합니다. 누군가가 작업중이거나 메인테이너가 작업을 원하지 않는 상태일 수 있으니 꼼꼼히 확인이 필요합니다.

추가적으로 spring-cloud 라이브러리도 살펴보았고, 하나의 이슈를 선정하게 되었습니다.

해당 이슈의 내용은 Spring Cloud OpenFeign의 OAuth2 설정에서 프로퍼티 이름 바인딩이 일관되지 않다는 내용입니다.

스프링 공식문서 - Relaxed Binding

공식문서 기준으로 Spring에서는 Relaxed Bindng을 지원합니다.

✔ 허용되는 변환

  • kebab-case (client-registration-id)
  • camelCase (clientRegistrationId)
  • snake_case (client_registration_id)
  • UPPER_SNAKE_CASE (CLIENT_REGISTRATION_ID)
  • 점 표기 (client.registration-id)

이 규칙은:

  • application.yml
  • application.properties
  • 환경 변수
  • JVM system properties

모두 동일하게 적용됩니다. 현재는 camelCase만 지원된다고 하니 한번 소스를 분석을 해봤습니다. 그 전에! 메인테이너를 호출해서 작업을 해도 되는지 물어봅니다.

허락을 받았으니 이제 오픈소스를 분석해봅니다!

2. 오픈소스 분석 & 수정

오픈소스를 수정하기 위해서는 아래 순서를 지킵니다.

1. 프로젝트 Fork

  • GitHub에서 대상 오픈소스 프로젝트를 내 계정으로 fork
  • 원본 저장소(upstream)는 그대로 두고, 내 fork 저장소(origin)에서 작업)

2. PC에 프로젝트 clone

git clone https://github.com/{my-account}/{repo}.git
cd {repo}

추가로 upstream 저장소 등록

git remote add upstream https://github.com/{original-org}/{repo}.git
git remote -v

-> 이후 최신 변경사항을 반영하기 위함입니다.

3. 작업용 브랜치 생성

  • main/master 에서 직접 작업 X
  • 이슈 단위 브랜치 생성
git checkout -b gh-1270-oauth2-dash-case-support

-> 브랜치명를 다양하게 만들지만 저는 github-이슈번호-간략한 이슈 내용 이렇게 생성하였습니다.

4. 개발 환경 세팅

  • 코드 포멧팅 설정
    -> Github에 가이드 문서대로 설정하면 됩니다.
  • 의존 관계 확인 후 다운로드
  • Spring Cloud OpenFeign은 단독 프로젝트가 아니라 Spring Cloud Commons와 Spring Cloud Build에 의존하는 구조이므로 해당 프로젝트들도 함께 준비합니다.

Spring Cloud OpenFeign은 Spring Cloud 생태계의 일부이기 때문에, 공통 기능을 담당하는 Commons와 공통 빌드 기준을 제공하는 Build 프로젝트를 구성해야 로컬에서 정상적인 개발과 테스트가 가능합니다.

이런 경험을 하면서 오픈소스와 더 가까워지는 느낌을 받았습니다. 👍👍

5. 소스코드 수정

오픈소스를 처음 수정하려고 하면,
어느 부분의 코드를 수정해야 하는지 막연하게 느껴질 수 있습니다.
프로젝트 규모가 크고 구조가 복잡할수록 진입 장벽은 더 높아집니다.

하지만 최근에는 AI의 도움을 받아 문제의 원인이 될 가능성이 높은 위치를 빠르게 파악할 수 있습니다.
이슈 설명과 증상을 기반으로 관련 모듈이나 클래스를 추려내고,
실제로 코드를 확인해야 할 범위를 크게 줄일 수 있었습니다.

또한 이번에 선택한 이슈의 경우,
이미 어느 클래스를 수정하면 될지에 대한 제안이 이슈에 포함되어 있었기 때문에,
해당 클래스를 중심으로 코드를 빠르게 탐색할 수 있었습니다.
그 덕분에 전체 코드를 무작정 살펴보는 대신,
문제와 직접적으로 연관된 부분에 집중하여 효율적으로 수정 작업을 진행할 수 있었습니다.

5.1.문제 원인 분석

먼저 기존 코드가 왜 dash-case를 지원하지 못하는지 파악했습니다.

기존 코드 (문제) :

@Bean
@ConditionalOnBean(OAuth2AuthorizedClientManager.class)
public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor(
        @Value("${spring.cloud.openfeign.oauth2.clientRegistrationId:}") String clientRegistrationId,
        OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
    return new OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientManager);
}

문제점:

  • @Value 어노테이션은 정확한 프로퍼티 이름만 매칭합니다.
  • 즉, clientRegistrationId는 작동하지만, client-registration-id는 인식하지 못합니다.
  • Spring Boot의 Relaxed Binding을 활용하지 못하고 있습니다.

프로젝트 내에 다른 설정 클래스들을 확인해보니, 대부분 @ConfigurationProperties를 사용하고 있었습니다.

@ConfigurationProperties("spring.cloud.openfeign.client")
public class FeignClientProperties { ... }

@ConfigurationProperties(prefix = "spring.cloud.openfeign.httpclient")
public class FeignHttpClientProperties { ... }

5.2 해결 방안 설계

문제를 해결하기 위해 다음과 같은 접근 방식을 선택했습니다.

  1. @ConfigurationProperties를 사용하는 Properties 클래스 생성
    • 다른 설정 클래스들과 동일한 패턴 적용
    • Spring Boot의 Relaxed Binding 활용
  2. 기존 @Value 방식을 Properties 클래스로 대체
    • 일관성 있는 설정 방식 유지
    • 타입 안정성 확보
  3. 테스트 추가
    • dash-case가 정상적으로 작동하는지 검증

5.3 변경사항 상세

변경한 클래스는 총 3개입니다:

1. FeignOAuth2Properties.java (신규 추가)

package org.springframework.cloud.openfeign;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * Configuration properties for Feign OAuth2 support.
 *
 * @author jaehun lee
 */
@ConfigurationProperties(prefix = "spring.cloud.openfeign.oauth2")
public class FeignOAuth2Properties {

    /**
     * Enables feign interceptor for managing oauth2 access token.
     */
    private boolean enabled = false;

    /**
     * Client registration id to be used to retrieve the OAuth2 access token. If not
     * specified, the {@code serviceId} retrieved from the {@code url} host segment will
     * be used. This is convenient for load-balanced Feign clients. For non-load-balanced
     * clients, specifying the {@code clientRegistrationId} is recommended.
     */
    private String clientRegistrationId = "";

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getClientRegistrationId() {
        return clientRegistrationId;
    }

    public void setClientRegistrationId(String clientRegistrationId) {
        this.clientRegistrationId = clientRegistrationId;
    }
}

핵심 포인트

  • @ConfigurationProperties 어노테이션으로 Relaxed Binding 활성화
  • prefix = "spring.cloud.openfeign.oauth2"로 설정 경로 지정
  • Getter/Setter를 통해 Spring Boot가 자동으로 값을 바인딩
  • 기존 FeignClientProperties, FeignHttpClientProperties와 동일한 패턴

Relaxed Binding이란?

Spring Boot는 다양한 형식의 프로퍼티 이름을 자동으로 변환해줍니다:

  • client-registration-id (dash-case) ✅
  • clientRegistrationId (camelCase) ✅
  • client_registration_id (snake_case) ✅
  • CLIENTREGISTRATIONID (대문자) ✅
    -> 모두 clientRegistrationId 필드로 매핑됩니다!

2. FeginAutoConfiguration.java (수정)

변경 1: @EnableConfigurationProperties에 추가

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ 
    FeignClientProperties.class, 
    FeignHttpClientProperties.class,
    FeignEncoderProperties.class, 
    FeignOAuth2Properties.class  // ← 추가!
})
public class FeignAutoConfiguration {
    // ...
}

변경2: Bean 메서드 파라미터 수정

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(OAuth2AuthorizedClientManager.class)
	@ConditionalOnProperty("spring.cloud.openfeign.oauth2.enabled")
	protected static class Oauth2FeignConfiguration {

		@Bean
		@ConditionalOnBean({ OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class })
		@ConditionalOnMissingBean
		OAuth2AuthorizedClientManager feignOAuth2AuthorizedClientManager(
				ClientRegistrationRepository clientRegistrationRepository,
				OAuth2AuthorizedClientService oAuth2AuthorizedClientService) {
			return new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository,
					oAuth2AuthorizedClientService);

		}

		@Bean
		@ConditionalOnBean(OAuth2AuthorizedClientManager.class)
		public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor(FeignOAuth2Properties oAuth2Properties,
				OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
			return new OAuth2AccessTokenInterceptor(oAuth2Properties.getClientRegistrationId(),
					oAuth2AuthorizedClientManager);
		}

	}

3. FeignAutoConfigurationTests.java (테스트 추가)

dash-case가 정상적으로 작동하는지 검증하는 테스트를 추가했습니다:

@Test
void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithDashCaseProperty() {
    runner
        .withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true",
                "spring.cloud.openfeign.oauth2.client-registration-id=feign-client")  // dash-case!
        .withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class))
        .withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class))
        .run(ctx -> {
            assertOauth2AccessTokenInterceptorExists(ctx);
            assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, "feign-client");
        });
}

테스트 목적

  • client-registration-id (dash-case) 형식이 정상적으로 인식되는지 확인
  • 기존 camelCase 테스트는 유지하여 하위 호환성 보장

5.4 동작 원리

Spring Boot의 프로퍼티 바인딩 과정:

1. application.yml 파일 읽기
   spring.cloud.openfeign.oauth2.client-registration-id: fancy

2. Spring Boot가 프로퍼티 정규화
   client-registration-id → clientregistrationid (소문자, 구분자 제거)

3. Java 필드명도 정규화
   clientRegistrationId → clientregistrationid

4. 매칭 성공!
   clientregistrationid == clientregistrationid ✅

5. Setter 호출
   setClientRegistrationId("fancy")

6. Bean으로 등록
   FeignOAuth2Properties 객체가 Spring 컨텍스트에 등록됨

7. 의존성 주입
   defaultOAuth2AccessTokenInterceptor 메서드의 파라미터로 자동 주입

5.5 사용자 관점에서의 변화

변경 전 (문제):

spring:
  cloud:
    openfeign:
      oauth2:
        clientRegistrationId: fancy      # ✅ 작동
        client-registration-id: fancy    # ❌ 작동 안 함!

변경 후 (해결):

spring:
  cloud:
    openfeign:
      oauth2:
        clientRegistrationId: fancy      # ✅ 작동 (하위 호환성 유지)
        client-registration-id: fancy    # ✅ 작동! (새로 지원)
        client_registration_id: fancy    # ✅ 작동! (보너스)

이제 사용자들은 Spring Boot의 표준 규칙에 따라 자유롭게 프로퍼티를 작성할 수 있습니다.

5.6 빌드 및 테스트

수정 완료 후 다음 명령어로 빌드와 테스트를 진행했습니다.

# 전체 빌드 (모든 테스트 포함)
./mvnw clean install

모든 테스트가 통과하여 기존 기능에 영향을 주지 않으면서 새로운 기능이 정상적으로 작동함을 확인했습니다.

6. Pull Request 제출

PR도 Spring Cloud 규칙에 맞게 제출했습니다.

7. 메인테이너의 Merged

메인테이너의 테스트 프로세스가 끝나면 머지를 시켜줍니다!
고맙다는 인사도 받아보는 값진 경험을 해보았습니다. 🎉🎉🎉

첫 오픈소스 기여를 마치며

Spring Cloud 오픈소스에 기여하면서 많은 것을 느끼고 배웠습니다.
처음에는
“내가 Spring Cloud 같은 대형 프로젝트에 기여할 수 있을까?”
라는 걱정이 앞섰지만, 지금 돌아보면 그런 생각은 기우였습니다.

오픈소스 기여 모임 운영진분들의 도움과 AI 활용, 그리고 꾸준한 노력만 있다면 오픈소스 기여는 생각보다 어렵지 않다는 것을 느꼈습니다. 무엇보다 그 과정에서 정말 많은 것을 배울 수 있었습니다.

스프링 공식 문서를 직접 찾아 읽고, 포맷과 규칙에 맞게 코드를 작성하기 위해 가이드 문서를 참고하며 환경을 세팅했습니다. 또한 다른 개발자들의 코드를 살펴보며 자연스럽게 프로젝트의 구조와 철학을 이해할 수 있었습니다.

Spring Cloud 팀의 반응도 예상보다 훨씬 긍정적이었습니다. 리뷰어들의 친절하고 건설적인 피드백 덕분에, 큰 오픈소스 프로젝트에 성공적으로 기여할 수 있었다고 생각합니다.

앞으로는 종종 취미 삼아 오픈소스에 기여해보려고 합니다. 제가 생각했던 것 이상으로 이 시간이 즐거웠기 때문입니다.

오픈소스 기여 경험이 없더라도, 의지만 있다면 충분히 도전해볼 만하다고 생각합니다. 이 글이 누군가에게 작은 계기가 되었으면 좋겠습니다. 감사합니다.

profile
성장하는, 나눌 줄 아는 개발자

1개의 댓글

comment-user-thumbnail
2026년 1월 16일

재훈님 글 엄청 꼼꼼하게 잘 작성해주셨네요 bb 재훈님의 글을 보고 오픈 소스 기여 시작하시는 분들께 가이드가 될 것 같습니다 ㅎㅎㅎ 다시한번 더 축하드립니다!!

답글 달기