🚩 1. 들어가며...
오늘 한 'U'회사에 면접을 보고 왔다. 빌드 도구인 Gradle(이하 그레이들)의 의존성 옵션에 대한 질문에 답변을 하지 못했다. 그동안 스프링 부트 스타터가 내가 선택한 라이브러리 의존성들을 너무 잘 구성해주었던 탓인지 신경쓰지 못한 부분이었다. 그래서 그레이들을 공부해보고자 포스팅을 시작한다.
🚩 2. Gradle이란?

- DSL(Domain-SpecificLanguage)
- Groovy나 Kotlin을 기반으로 한 DSL(Domain-SpecificLanguage)을 사용하여 빌드 스크립트를 작성하는 빌드 툴이다.
- XML 문법보다 가독성이 좋고 간결하다.
- 빌드 캐싱
- 빌드 작업을 캐싱하여 반복 빌드 시간을 크게 단축시킬 수 있다.
- 불필요한 빌드 작업을 피하고 빠른 개발 사이클 제공한다.
- 멀티 프로젝트 빌드
- 여러 개의 하위 프로젝트를 포함하는 대규모 멀티 프로젝트 빌드를 지원한다.
- 여러 모듈 또는 서브 프로젝트 간에 의존성을 관리하고 병렬로 빌드를 수행할 수 있다.
🚩 3. 의존성 옵션
❗면접에서 받았던 질문에 관한 부분이다.
그레이들에서는 다양한 의존성 옵션을 제공하여 프로젝트의 의존성을 세밀하게 제어할 수 있다. 각각의 옵션은 프로젝트의 다양한 단계(컴파일, 런타임, 테스트 등)에서 필요한 의존성을 조절할 때 사용한다. 아래는 실제 프로젝트에서 사용한 의존성들 중 일부이다.
implementation 'org.springframework.boot:spring-boot-starter-security'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'junit:junit:4.13.1'
여기서 의존성 옵션이란 가장 앞에서 선언한 'implementation', 'compileOnly', 'runtimeOnly', 'developmentOnly,' 'annotationProcessor', 'testImplementation'부분이다. 이외에도 'api', 'testCompileOnly', 'testRuntimeOnly' 등이 있다.
📌 3-1. 각 옵션의 특징
- implementation
- 프로젝트의 컴파일 시간과 런타임에 의존성을 사용한다.
- 해당 모듈의 API를 통해 다른 모듈에 노출되지 않는다.
- 프로젝트 내부에서만 사용하고 외부 모듈에 노출할 필요가 없을 때 사용하기 적절하다.
- api
- 프로젝트의 컴파일 시간과 런타임에 의존성을 사용한다.
- 해당 모듈을 의존하는 다른 모든 모듈에 노출, 공개 API의 일부가 된다.
- 현재 모듈이 사용하고 있는 의존성을, 현재 모듈을 의존하는 다른 모듈들과 함께 사용할 때 적절하다.
- compileOnly
- 컴파일 시에만 필요한 의존성을 사용한다. 컴파일 시 클래스패스에 포함한다.
- 컴파일 시에만 필요한 도구나 라이브러리(예: Lombok, AnnotationProcessor)에 사용하기 적절하다.
- runtimeOnly
- 런타임 시에만 필요한 의존성을 사용한다. 런타임 시 클래스패스에 포함한다.
- 런타임 시에만 필요한 도구나 라이브러리(예: JDBC 드라이버)에 사용하기 적절하다.
- developmentOnly
- 개발 환경에서만 필요한 의존성을 사용한다.
- 개발도구나 로컬 테스트 서버 등 개발 시에만 필요한 경우에 사용하기 적절하다.
- annotationProcessor
- 컴파일 시 어노테이션 프로세싱을 위한 의존성을 사용한다.
- 컴파일러가 소스 코드에서 어노테이션을 처리하는 데 사용하고, 이를 통해 추가 코드를 생성하거나 기존 코드를 수정할 수 있다.
- Lombok, Room, Dagger와 같이 컴파일 시 어노테이션을 처리하는 라이브러리에 사용하기 적절하다.
- testImplementation
- 테스트 컴파일과 런타임에 필요한 의존성을 사용한다.
- 테스트 코드에 한해서 사용하고 일반 애플리케이션 코드에서는 적용되지 않는다.
- JUnit, Mockito, Espresso 등 테스트를 위해 사용하는 라이브러리에 사용하기 적절하다.
- 'testCompileOnly', 'testRuntimeOnly'는 앞과 비슷한 맥락으로 생략한다.
🚩 4. implementation옵션과 api옵션의 전파성 차이
옵션에 대해서 보다보면, Implementation과 api옵션에서 외부 모듈에 노출 여부에 대한 이야기가 나온다. 이 얘긴 의존성의 전파성에 대한 이야기이다. 만약 'some-library'라는 외부 라이브러리를 의존하는 모듈A가 있다고 가정하고, 또다시 이 모듈A를 의존하는 B모듈과 C모듈이 있다고 생각해보자. 이때 A모듈에서 'some-library'의 라이브러리 의존성 옵션을 Implementation, api 각각 다르게 했을 때의 차이점을 알아보자.
📌 4-1. implementation - 비노출, 비전파
dependencies {
implementation 'com.other.library:some-library:2.0.0' // 외부 라이브러리
}
- implementation으로 설정한 의존성은 직접적으로 의존하는 모듈A 내부에서만 사용하며, B와 C모듈에는 전파되지 않는다.
- 이 말은 'some-library'가 버전이 바뀌거나 변동 사항이 있을 때 직접적으로 의존하는 모듈A 재컴파일하고 모듈 B, C는 재컴파일 할 필요가 없다.
- 빌드 시간을 단축시키고, 내부 구현을 숨기고 모듈 간 결합도를 낮추는데 유용하다.
📌 4-2. api - 노출, 전파
dependencies {
api 'com.other.library:some-library:2.0.0' // 외부 라이브러리
}
- api로 설정한 의존성은 직접적으로 의존하는 모듈A 뿐만 아닌 간접적으로 의존하는 모든 모듈에 전파된다.
- 이 말은 'some-library'가 버전이 바뀌거나 변동 사항이 있을 때 직/간접적으로 의존하는 모듈A, B, C 모두 재컴파일할 필요가 있다.
- 공통 의존성을 모듈 간에 공유하고자 할 때 유용하다.
🚩 5. 마치며...
면접을 보며 빌드 툴에 대한 질문은 처음 받아봤는데 덕분에 내가 빌드 툴에 대해서 잘 모르고 있다는 걸 깨달았다. 왜 개발 서적과 선배들이 처음부터 스타터로 모든 걸 해결하지 말고 필요한 의존성을 직접 추가해보라는 지 알게 됐다. 아직 회사에서는 연락이 오지 않았지만, 긴 시간 코드 인터뷰도 해보고 몰랐던 부분에 대해서도 인지할 수 있는 좋은 경험이 되었다. 다음에는 또 다른 빌드 툴인 'Maven'에 대해서도 포스팅해 봐야겠다.