ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

gradle.properties

도입

빌드 스크립트에 흩어지기 쉬운 로컬 설정, CI 입력값을 한곳에서 관리해 재현성과 운영 편의성을 동시에 확보하는 데 있다

gradle.properties는 겉보기에는 단순한 key-value 파일이지만, 실제로는 Gradle 빌드 환경을 안정적으로 고정하는 중요한 진입점입니다.

빌드 캐시, 병렬 실행, 데몬 JVM 메모리, 프록시, 프로젝트별 커스텀 플래그, CI 비밀값 주입까지 이 파일 또는 그와 연결된 속성 체계로 정리할 수 있습니다.

그래서 build.gradle(.kts)만 볼 줄 아는 수준과 gradle.properties까지 설계할 줄 아는 수준은 실무 감각에서 차이가 꽤 큽니다.

필요성

같은 프로젝트가 개발자 PC, CI, IDE, 배포 서버에서 최대한 같은 방식으로 실행되며 빌드 성능과 운영 안정성도 함께 잡을 수 있다

Gradle은 command line 옵션, 환경 변수, 시스템 속성, 프로젝트 속성, Gradle 전용 속성처럼 설정 경로가 많습니다. 이 구조를 모르고 빌드를 운영하면 누군가는 터미널 옵션에 의존하고, 누군가는 IDE 설정에 의존하고, 누군가는 로컬 파일에만 의존하는 식으로 설정이 퍼집니다.

반면 gradle.properties를 기준점으로 잡으면 무엇을 팀 공용으로 공유할지, 무엇을 개인 로컬에만 둘지, 무엇을 CI 환경 변수로 주입할지 경계가 분명해집니다. 실무에서 이 분리가 잘 되어 있을수록 빌드가 덜 흔들리고 디버깅도 쉬워집니다.

gradle.properties가 특히 중요한 이유
  • 빌드 캐시, 병렬 실행, JVM 메모리 같은 공통 플래그를 고정할 수 있음
  • 개인 PC와 CI의 설정을 의도적으로 분리할 수 있음
  • 프로젝트 속성을 통해 빌드 로직을 외부 입력값으로 제어할 수 있음
  • 프록시, 인증, 서명 정보 같은 운영성 요소를 체계적으로 배치할 수 있음

정의

gradle.properties는 하나의 파일명이지만, 실제로는 Gradle 전용 속성·JVM 시스템 속성·프로젝트 속성을 함께 담을 수 있는 설정 허브에 가깝다

많은 사람이 gradle.properties를 그냥 “Gradle 옵션 넣는 파일” 정도로 이해하지만, 실제로는 용도가 다른 속성들을 함께 수용할 수 있습니다. 이 차이를 구분하지 않으면 왜 어떤 값은 빌드 스크립트에서 읽히고, 어떤 값은 Gradle 자체 동작에만 영향을 주는지 이해하기 어려워집니다.

구분 형태 주 용도 예시
Gradle properties org.gradle.* Gradle 자체 동작 제어 org.gradle.jvmargs, org.gradle.caching, org.gradle.parallel
System properties systemProp.* JVM / 네트워크 / 시스템 레벨 설정 systemProp.http.proxyHost, systemProp.https.proxyPort
Project properties 일반 key 또는 org.gradle.project.* 빌드 로직에 전달할 사용자 정의 값 isCI=true, repoUser=..., kotlin.code.style=official
중요한 구분
org.gradle.* 속성은 Gradle 자체를 조정하는 값이고, build logic에서 읽기 위한 값은 보통 project property로 두는 편이 맞습니다. 즉, 팀이 직접 읽어 쓸 입력값과 Gradle 엔진 자체 설정은 분리해서 생각해야 합니다.

핵심 원리

gradle.properties를 잘 쓰는 핵심은 값을 파일에 넣는 행위 자체가 아니라, 그 값이 팀 공용인지·개인 전용인지·CI 전용인지 먼저 분류하는 데 있다

같은 gradle.properties라는 이름이라도 어디에 두는지에 따라 의미가 달라집니다. 그래서 실무에서는 “어디에 넣을까”가 “무슨 값을 넣을까”만큼 중요합니다.

  1. 팀 전체가 공유해야 하는 값이면 프로젝트 루트 gradle.properties에 둔다.
  2. 내 PC에만 필요한 값이면 GRADLE_USER_HOME 아래의 사용자용 gradle.properties에 둔다.
  3. 비밀값이나 CI 전용 값이면 환경 변수, 특히 ORG_GRADLE_PROJECT_* 계열을 먼저 고려한다.
  4. 빌드 스크립트에서 읽어야 하는 값이면 project property로 두고 providers.gradleProperty()로 접근한다.

결국 gradle.properties 설계는 “값 저장”의 문제가 아니라 설정의 책임 분리 문제입니다. 이 구분이 명확하면 코드 리뷰, 보안 관리, CI 설정, 빌드 재현성이 전부 좋아집니다.

우선순위와 탐색 순서

gradle.properties를 어려워하는 가장 큰 이유는 문법이 아니라 우선순위이며, 어떤 종류의 속성이냐에 따라 덮어쓰기 규칙도 달라진다
대상 우선순위 요약 실무 해석
Gradle properties -D > GRADLE_USER_HOME > 프로젝트 루트 > GRADLE_HOME 개인 로컬 설정이 루트 설정을 덮을 수 있음
Project properties -P > -Dorg.gradle.project.* > gradle.properties > ORG_GRADLE_PROJECT_* 빌드 호출 시점 입력이 가장 강함
System properties -D 또는 systemProp.* 프록시, TLS, wrapper 다운로드 인증처럼 JVM 쪽 설정에 적합

여기서 특히 많이 놓치는 포인트가 두 가지입니다. 첫째, 같은 파일명이라도 GRADLE_USER_HOME의 사용자용 파일이 프로젝트 루트 파일보다 우선할 수 있습니다. 둘째, 멀티 프로젝트에서 systemProp.*는 프로젝트 트리 안에서는 root 프로젝트의 gradle.properties만 본다고 생각하는 편이 안전합니다.

기본 구현

처음 설계할 때는 팀 공용 플래그·프로젝트 입력값·시스템 속성을 하나의 예시 파일로 구분해 보는 것이 가장 이해가 빠르다
# Gradle properties
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8

# 호환성 검토 후 점진적으로 켜는 편이 안전
# org.gradle.configuration-cache=true

# System properties
systemProp.http.proxyHost=proxy.company.local
systemProp.http.proxyPort=8080

# Project properties
kotlin.code.style=official
isCI=false
repoUrl=https://repo.example.com/releases

빌드 스크립트에서 이런 값을 읽을 때는 즉시 평가보다 lazy access 쪽이 더 자연스럽습니다. 특히 Kotlin DSL에서는 providers.gradleProperty()를 자주 사용합니다.

tasks.register("printFlags") {
    val isCI = providers.gradleProperty("isCI")
    val repoUrl = providers.gradleProperty("repoUrl")

    doLast {
        println("isCI = " + isCI.orNull)
        println("repoUrl = " + repoUrl.orNull)
    }
}
실전 팁
org.gradle.jvmargs는 Gradle Daemon JVM에 적용되는 값입니다. 클라이언트 VM과 데몬 JVM의 역할을 섞어 생각하면 메모리 설정이 왜 기대대로 안 먹는지 헷갈리기 쉽습니다.

패턴 1. 팀 공용 빌드 플래그

모든 개발자와 CI가 같은 방식으로 빌드되길 원한다면 루트 gradle.properties에 공통 플래그를 두고 버전 관리에 포함시키는 방식이 가장 안정적이다

이 패턴은 팀 전체가 반드시 동일하게 따라야 하는 설정을 공유할 때 씁니다. 대표적으로 build cache, 병렬 실행, 공통 인코딩, warning policy 같은 값이 여기에 들어갑니다.

핵심은 누구도 매번 -D 옵션을 기억할 필요가 없게 만드는 것입니다. command line은 테스트와 디버깅에는 좋지만, 영구적인 팀 설정 저장소로는 잘 맞지 않습니다.

루트 gradle.properties에 넣기 좋은 값
  • org.gradle.caching=true
  • org.gradle.parallel=true
  • org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
  • 팀이 합의한 프로젝트 속성 예: isCI=false, repoUrl=...

다만 org.gradle.configuration-cache=true는 무조건 넣기보다는 현재 build logic과 plugin 호환성을 먼저 확인하고 점진적으로 켜는 편이 현실적입니다. 성능상 이점은 크지만, 모든 기존 빌드가 자동으로 문제없이 따라오는 것은 아닙니다.

패턴 2. 로컬 개발자 설정과 개인 비밀값

개인 PC 전용 설정이나 서명 정보처럼 공유하면 안 되는 값은 프로젝트 루트가 아니라 사용자용 gradle.properties로 분리하는 편이 정석에 가깝다

사용자 전용 gradle.properties는 보통 ~/.gradle/gradle.properties 또는 Windows의 사용자 홈 아래 .gradle\gradle.properties에 둡니다. 이 위치는 개인 PC의 JDK 경로, 프록시, 서명 키 경로처럼 팀 전체에 공유할 필요가 없는 값을 넣기에 좋습니다.

# ~/.gradle/gradle.properties

org.gradle.java.home=/Users/me/.jdks/temurin-21

signing.keyId=24875D73
signing.password=********
signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg

systemProp.https.proxyHost=proxy.company.local
systemProp.https.proxyPort=8443

이 패턴의 장점은 분명합니다. 저장소에는 남기면 안 되는 값은 로컬에만 남고, 프로젝트 루트는 팀 공용 설정만 유지합니다. 특히 publish 서명, 사내 프록시, 개인 JDK 설치 경로는 이 방식이 가장 깔끔합니다.

패턴 3. CI 와 ORG_GRADLE_PROJECT 환경 변수

CI에서는 비밀값을 프로젝트 파일에 넣기보다 ORG_GRADLE_PROJECT 접두사 환경 변수로 project property에 매핑하는 방식이 가장 실용적이고 안전하다

CI 서버는 보통 환경 변수를 안전하게 저장하고 주입하는 기능을 제공합니다. 그래서 저장소에 비밀번호를 남기기보다, CI에서 ORG_GRADLE_PROJECT_repoUser 같은 이름으로 환경 변수를 넣고 build script에서는 평범한 project property처럼 읽는 방식이 많이 쓰입니다.

ORG_GRADLE_PROJECT_repoUser=ci-user
ORG_GRADLE_PROJECT_repoPassword=ci-password
ORG_GRADLE_PROJECT_signingKey=-----BEGIN PGP PRIVATE KEY BLOCK-----
ORG_GRADLE_PROJECT_signingPassword=secret
publishing {
    repositories {
        maven {
            credentials {
                username = providers.gradleProperty("repoUser").orNull
                password = providers.gradleProperty("repoPassword").orNull
            }
        }
    }
}

이 방식의 장점은 build script가 로컬 파일 경로에 의존하지 않고도 CI 비밀값을 받을 수 있다는 점입니다. 특히 signing key, repository credential, release flag처럼 자동화 환경에서만 필요한 값에 잘 맞습니다.

혼동하기 쉬운 파일

gradle.properties와 gradle-wrapper.properties를 혼동하면 빌드 동작 설정과 wrapper 배포 설정이 뒤섞여 관리 포인트가 흐려진다
파일 주 역할 대표 내용
gradle.properties Gradle / 시스템 / 프로젝트 속성 관리 org.gradle.jvmargs, org.gradle.caching, systemProp.http.proxyHost, isCI
gradle/wrapper/gradle-wrapper.properties Wrapper 런타임과 배포 URL 관리 distributionUrl=...

즉, Gradle 버전과 wrapper 배포 URL은 gradle-wrapper.properties의 책임이고, 프록시나 공통 build flag 같은 일반 설정은 gradle.properties 쪽 책임입니다. 이름이 비슷하다고 같은 용도로 쓰면 안 됩니다.

자주 하는 실수

gradle.properties가 엉키는 이유는 문법 오류보다도 속성 종류와 저장 위치를 뒤섞어 쓰기 때문인 경우가 훨씬 많다
  • 비밀번호와 서명 키를 프로젝트 루트 gradle.properties에 넣고 그대로 커밋함
  • org.gradle.* 값을 build logic에서 직접 읽어 쓰려 함
  • 멀티 프로젝트의 서브모듈마다 gradle.properties를 두고 설정이 섞이게 만듦
  • systemProp.*를 서브프로젝트에 두고 왜 적용되지 않는지 헷갈림
  • gradle.propertiesgradle-wrapper.properties의 역할을 혼동함
  • 호환성 확인 없이 configuration cache를 전역으로 켜서 plugin 문제를 만남
  • 문제 원인을 확인하려고 비밀값을 그대로 로그에 출력함

실무 루틴

새 속성을 추가할 때는 키 이름부터 정하는 것이 아니라, 이 값이 누구를 위한 것인지와 어디까지 공유되어야 하는지를 먼저 결정해야 한다
  1. 이 값이 Gradle 자체 설정인지, build logic 입력값인지 먼저 나눈다.
  2. 팀 공용인지, 개인 로컬인지, CI 전용인지 범위를 정한다.
  3. 공유 값이면 루트 gradle.properties, 개인 값이면 사용자 홈, 비밀값이면 환경 변수를 우선 검토한다.
  4. 빌드 스크립트에서 읽을 값이면 providers.gradleProperty() 같은 lazy access 방식으로 연결한다.
  5. 보안 값은 출력 로그와 버전 관리 포함 여부를 마지막으로 다시 확인한다.

디버깅

gradle.properties 문제를 디버깅할 때는 값 자체보다 먼저 이 값이 project property인지 system property인지, 그리고 어느 파일이 최종 승자가 되었는지부터 확인해야 한다
1
해당 키가 org.gradle.*, systemProp.*, project property 중 무엇인지부터 다시 본다.
2
루트 파일, 사용자 홈 파일, 환경 변수, command line 중 어디에서 덮였는지 추적한다.
3
멀티 프로젝트라면 systemProp.*가 서브프로젝트 파일에 들어가 있지 않은지 확인한다.
4
디버그용 task를 만들어 존재 여부만 확인하고, 비밀값은 원문을 그대로 찍지 않는다.
tasks.register("debugProps") {
    doLast {
        println("isCI = " + providers.gradleProperty("isCI").orNull)
        println("repoUser exists = " + (providers.gradleProperty("repoUser").orNull != null))
        println("JAVA_HOME = " + providers.environmentVariable("JAVA_HOME").orNull)
        println("https.protocols = " + providers.systemProperty("https.protocols").orNull)
    }
}

요약

gradle.properties의 본질은 단순한 설정 파일이 아니라 빌드 실행 정책과 외부 입력값의 경계를 정리하는 운영 설계이며, 어디에 어떤 종류의 속성을 둘지 아는 것이 실무 역량의 핵심이다
  • gradle.properties에는 Gradle properties, system properties, project properties를 함께 둘 수 있다.
  • ✅ 팀 공용 값은 루트 gradle.properties에 두고 버전 관리에 포함하는 편이 좋다.
  • ✅ 개인 로컬 설정과 민감 정보는 사용자 홈 ~/.gradle/gradle.properties가 더 적합하다.
  • ✅ CI 비밀값은 ORG_GRADLE_PROJECT_* 환경 변수로 주입하면 깔끔하다.
  • org.gradle.jvmargs, org.gradle.caching, org.gradle.parallel 같은 값은 성능과 운영에 직접 영향이 크다.
  • systemProp.*와 project property를 혼동하면 디버깅이 어려워진다.
  • gradle.propertiesgradle-wrapper.properties는 역할이 전혀 다르다.
728x90