이 글은 1편에서 잡은 큰그림 중, GitHub Actions의 build job을 “Runner 내부에서 실제로 어떤 일이 벌어지는지” 기준으로 풀어낸다.
이번 2편에서 다루는 범위는 아래와 같다.
- Set up JDK 17
- Gradle Caching (actions/cache)
- Grant execute permission for gradlew
- Build with Gradle (./gradlew build -x test)
- (트러블 슈팅) Gradle Wrapper가 왜 필요한지 (gradle-wrapper.jar / gradle-wrapper.properties)
- (트러블 슈팅) 로컬에서 wrapper 생성하다 터진 오류 + 해결(SDKMAN, zip/unzip, 전역 gradle 버전 문제, JDK toolchain 문제)
3편은 Docker build/push(로그인/태그/캐시/도커파일), 4편은 SSM 배포/시크릿/운영/트러블슈팅을 다룬다.
build job의 핵심 흐름
build job은 아래 순서로 진행된다.
- repository checkout
- Set up JDK 17
- Gradle Caching
- gradlew 실행 권한 부여
- ./gradlew build -x test
이 단계들은 “그냥 실행되는 명령 나열”이 아니라, Runner가 매번 새로 시작되는 깨끗한 VM이라는 전제를 깔고 봐야 한다.
1) Set up JDK 17
- name: repository checkout
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
Github Actions 가상 머신(Runner)에 특정 버전의 Java(JDK)를 설치하고, 즉시 사용할 수 있도록 환경 변수를 설정하는 스텝이다.
./gradlew build와 같은 Java 기반의 빌드 명령어를 실행하려면, 가상 머신에 먼저 Java가 설치되어 있어야 한다.
항목 설명
- uses: actions/checkout@v3
- 가상 머신에 내 프로젝트 코드(소스 코드, gradlew 파일, build.gradle등)를 다운로드하는 역할을 하기 때문이다.
- Github Actions 워크플로우가 실행되면, GitHub는 아무것도 설치되지 않은 깨끗한 가상 머신을 빌려준다. 이 가상 머신에는 내 프로젝트 코드가 아무것도 없기 때문에 actions/checkout@v3을 통해서 가져와야 한다.
- uses: actions/setup-java@v3
- 이 스텝에서 사용할 공식 액션을 지정한다.
- actions/setup-java는 가상 머신(Runner)에 특정 버전의 Java(JDK)를 설치하고 환경 변수(JAVA_HOME, PATH)를 설정해 주는 스크립트 묶음이다.
- JAVA_HOME: 방금 설치한 JDK 17의 경로로 설정합니다.
- PATH: java, javac 등 JDK 17의 실행 파일이 있는 bin 폴더 경로를 PATH에 추가합니다.
- java-version: '17'
- 설치할 JDK의 메인 버전을 지정한다.
- distribution: 'temurin'
- 설치할 JDK의 배포판(Distribution)을 지정한다.
- Java(JDK)는 Oracle JDK 외에도 다양한 기관에서 빌드하여 배포한다.
- temurin은 Eclipse Temurin 배포판을 의미한다.
이 스텝이 필요한 이유
- 환경의 일관성 보장:
내 로컬 PC에서 JDK 17로 개발했다면, 빌드 서버(GitHub Actions)에서도 정확히 JDK 17로 빌드해야 한다. 이 스텝은 버전을 '17'로 고정하여 내 PC에선 됐는데 서버에선 안 되는 것과 같은 문제를 방지합니다. - 빌드 준비:
./gradlew build 명령어는 실행될 때 JAVA_HOME 환경 변수를 찾아 해당 Java를 사용한다. 이 스텝이 JAVA_HOME을 미리 설정해 주기 때문에, 바로 다음 스텝인 Gradle 빌드가 JDK 17을 사용하여 정상적으로 동작할 수 있다.
2) Gradle Caching
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
이 코드는 Github actions에서 Gradle 빌드 속도를 높여주는 캐시 설정이다.
gradle은 build.gradle 파일에 선언된 의존성(라이브러리) 목록을 보고, Gradle이 Maven Central 같은 원격 저장소에 접속해서 필요한 파일(.jar)들을 다운로드한다.
만약 key에 해당하는 캐시가 있다면 경로의 파일을 다운로드하여 그대로 복원한다.
만약 캐시가 없다면 일단 그냥 넘어간다. (build 단계에서 다운 받음)
매번 빌드할 때마다 라이브러리(dependency)를 인터넷에서 새로 다운로드하는 대신, 이전에 다운로드한 파일들을 GitHub Actions 캐시 스토리지에 저장했다가 다음 빌드에 재사용하는 기능이다.
항목 설명
1. uses: actions/cache@v3
캐싱을 수행하는 공식 액션
- 주로 의존성이나 빌드 결과물처럼 매번 새로 다운로드하거나 생성하기에 시간이 오래 걸리는 것들을 캐시하는 데 사용
2. with:
actions/cache 액션에 전달할 설정값(파라미터)들
3. path: (캐시할 대상)
캐시로 저장할 파일 및 폴더의 경로를 지정
- ~/.gradle/caches
- Gradle이 인터넷에서 다운로드한 모든 라이브러리 파일(jar, pom 등)이 저장되는 핵심 폴더
- ~/.gradle/wrapper
- Gradle 자체 실행 파일이 저장되는 폴더이다.
- ~/.gradle/caches에는 의존성 라이브러리(jar/pom)가 쌓인다.
- ~/.gradle/wrapper에는 Gradle 툴 본체 배포판(예: gradle-8.x-bin.zip)이 내려받혀 저장된다.
- 즉 전자는 “프로젝트가 쓰는 라이브러리 캐시”, 후자는 “Gradle 자체를 실행하기 위한 캐시”다.
4. key:
캐시를 저장하고 불러올 때 사용할 식별자 ID
이 key값이 일치하는 캐시가 있을 때만 해당 캐시를 불러온다.(캐시 히트)
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- runner.os:
- 워크플로우가 실행되는 가상 머신의 운영체제(OS)이다. (예: Linux , Windows)
- OS마다 캐시 파일 구조가 다를 수 있으므로 OS별로 다른 캐시를 만들기 위해 사용한다.
- gradle- :
- 단순히 키의 이름을 구분하기 위한 접두사이다.
- hashFiles(...) :
- hashFiles 함수는 괄호 안의 파일들의 내용(Content)을 읽어 고유한 해시 값을 생성한다.
- ‘**/*.gradle*’ : build.gradle, settings.gradle, build.gradle.kts 등 .gradle로 끝나는 모든 파일 (즉, 의존성이 변경되면 이 파일 내용이 바뀐다.)
- '**/gradle-wrapper.properties' : Gradle 버전 정보가 담긴 파일 (즉, Gradle 버전이 바뀌면 이 파일 내용이 바뀐다.)
동작 방식:
- 만약 build.gradle 파일에 라이브러리를 하나 추가하면?
- hashFiles 값이 변경된다.
- key 전체 값이 변경된다. (예: Linux-gradle-abc123... → Linux-gradle-xyz789...)
- Github Actions는 새로운 key 에 해당하는 캐시가 없다고 판단한다.
- 만약 코드는 바꿨지만 build.gradle은 그대로라면?
- hashFiles 값이 변경되지 않는다.
- key 값이 이전 빌드와 동일하다.
- Github Actions는 정확히 일치하는 캐시(캐시 히트)를 찾아 path에 지정된 폴더/파일들을 즉시 복원한다.
5. restore-keys: (캐시 조회를 위한 대체 키)
이것은 key로 정확한 캐시를 찾지 못했을 때 사용되는 Plan B이다.
key는 완벽하게 일치해야 하지만, restore-keys는 이걸로 시작하는 키중에서 가장 최근에 생성된 캐시를 찾아 복원한다.
restore-keys는 전부 다 다운로드하는 최악의 상황을 부족한 부분만 다운로드하는 최선의 상황으로 바꿔준다.
동작 방식:
- [최초 빌드]
- key (예: Linux-gradle-abc123...)에 맞는 캐시가 당연히 없다.
- restore-keys (Linux-gradle-)로 시작하는 캐시도 당연히 없다.
- 빌드를 처음부터 진행하고, 모든 라이브러리를 다운로드한다.
- 빌드 성공 후, key ( Linux-gradle-abc123...) 이름으로 path의 파일들을 캐시에 저장한다.
- [코드만 수정 후 두 번째 빌드]
- build.gradle이 안 바뀌었으므로 key는 Linux-gradle-abc123...로 동일하다.
- GitHub가 이 key로 캐시를 검색한다.
- 캐시 히트
- 1번에서 저장한 캐시를 즉시 복원한다. 라이브러리를 전혀 다운받지 않아 빌드 시간이 매우 빨라진다.
- [라이브러리 1개 추가 후 세 번째 빌드]
- build.gradle 파일이 변경되었다.
- key가 (예: Linux-gradle-xyz789...)로 변경되었다.
- GitHub가 새로운 key로 캐시를 검색한다.
- 정확히 일치하는 캐시가 없다.
- 이때 restore-keys:가 동작한다.
- Linux-gradle-로 시작하는 캐시를 검색한다.
- 1번 빌드에서 저장한 Linux-gradle-abc123... 캐시가 검색된다. (가장 최신 캐시)
- 이 이전 캐시를 일단 복원한다. ('Partial Cache Hit’)
- Gradle이 빌드를 시작한다.
- 이미 99%의 라이브러리가 복원되었으므로, 새로 추가된 1개의 라이브러리만 다운로드한다.
- 빌드 성공 후, 새로운 key ( Linux-gradle-xyz789...) 이름으로 현재 path의 파일들을 캐시에 새로 저장한다.
동작 원리
1. 가상 머신 시작
- 워크플로우가 트리거되면(ex: push), Github는 매번 깨끗하게 새로 설치된 가상 머신(VM, Runner)을 띄운다.
- 이 가상 머신은 아무것도 설치되어 있지 않은 빈 상태이며, ~/.gradle/caches 폴더도 비어있다.
(내 로컬 PC의 파일들과는 상관x)
2. 캐시 복원
- actions/cache@v3 스텝 실행
- key 또는 restore-keys와 일치하는 캐시가 Github의 캐시 저장소에 있는지 검색
- 만약 일치하는 캐시를 찾으면 해당 캐시 파일을 다운로드하여 가상 머신의 path:에 지정된 경로(~/.gradle/caches, ~/.gradle/wrapper)에 압축을 풀어 파일을 복원시킨다.
3. 빌드 실행 (./gradlew build)
- 이제 Gradle 빌드 명령어가 가상 머신 내부에서 실행된다.
- (만약 2번에서 캐시 복원에 성공했다면) Gradle은 ~/.gradle/caches 폴더에 이미 라이브러리 파일들이 존재하는 것을 확인하고, 인터넷에서 새로 다운로드하는 과정을 생략
- (만약 2번에서 캐시 복원에 실패했다면) Gradle은 필요한 파일들을 인터넷에서 다운로드하여 가상 머신의 ~/.gradle/caches 폴더에 저장한다.
4. 캐시 저장
- 캐시 복원 단계에서 actions/cache 액션이 실행될 때, GitHub Actions 시스템에 post-job(잡 종료 후) 작업을 등록한다.
- 이 post-job 작업은 Build with Gradle 스텝을 포함한 모든 run 스텝이 완료된 후, 가상 머신이 종료되기 직전에 자동으로 실행된다.
- 2번에서 사용했던 key와 일치하는 캐시가 Github 캐시 저장소에 이미 있는지 확인
- 만약 일치하는 캐시가 없다면(주로 3번에서 새로 다운로드한 경우): 현재 가상 머신(Runner)의 path: 경로(~/.gradle/caches 등)에 있는 모든 파일들을 압축하여 key와 함께 GitHub 캐시 저장소에 업로드한다. (다음 빌드에서 2번 단계에서 사용하기 위해서)
3) Grant execute permission for gradlew
- name: Grant execute permission for gradlew
run: chmod +x gradlew
working-directory: oneco
Runner는 매번 새 VM이라서, 체크아웃된 파일들의 실행 권한이 기대와 다를 수 있다.
그래서 ./gradlew를 실행하기 전에 chmod +x gradlew로 실행 권한을 부여한다.
4) Build with Gradle
- name: Build with Gradle
run: ./gradlew build -x test
working-directory: oneco
gradle을 다운받고 build.gradle 파일에 있는 의존성을 다운로드한다.
동작과정
(1) Gradle Wrapper 스크립트 실행( gradle 툴 다운로드)
- 프로젝트에서 gradle 명령어를 직접 쓰지 않고 ./gradlew를 사용하면, Gradle Wrapper를 사용하는 것이다.
- 동작 원리
- ./gradlew build 명령어를 실행하면, 작은 스크립트(gradlew) 가 먼저 실행된다.
- gradlew는 프로젝트 레포지토리에 있는 파일
- 이 파일은 uses: actions/checkout@v4 스텝이 실행될 때 내 프로젝트 레포지토리에 있는 다른 모든 코드(build.gradle , src 폴더 등)와 함께 가상머신으로 다운로드(체크아웃)된다.
(현재 워크플로우는 v3이므로, 표현은 “checkout@v3”(수정))
- 이 스크립트는 프로젝트 내의 gradle/wrapper/gradle-wrapper.properties 파일을 읽어 이 프로젝트에 필요한 Gradle 버전을 확인한다.
- 가상머신에 해당 버전의 Gradle이 없으면, Wrapper 스크립트가 ~/.gradle/wrapper/dists에 배포판을 다운로드·압축해제한다.
- 그렇게 다운로드한 Gradle을 사용하여 실제 build 작업을 수행한다.
- ./gradlew build 명령어를 실행하면, 작은 스크립트(gradlew) 가 먼저 실행된다.
- 장점
- 개발자 PC든, github 가상머신이든, 어떤 환경에서도 정확히 동일한 버전의 Gradle 사용을 보장한다.
- 가상머신에 미리 Gradle을 다운로드할 필요가 없다. gradlew 스크립트만 실행하면 알아서 다운로드한다.
(2) 라이브러리 다운로드
- 이제 다운로드·압축해제된 Gradle 툴이 실행된다.
- Gradle 툴은 checkout으로 받아온 build.gradle 파일(로컬 파일) 및 settings.gradle을 읽는다.
- Gradle 툴은 원래 자신의 캐시 폴더인 ~/.gradle/caches를 확인한다.
- 이 폴더가 이미 actions/cache에 의해 채워져 있으면, 필요한 라이브러리가 이미 존재한다고 판단하고 다운로드를 건너뛴다.
- 없으면, 그제야 Gradle 툴이 원격 저장소(Maven Central 등)에서 라이브러리들을 다운로드한다.
-x test 옵션
- -x : --exclude-task의 축양형으로, 특정 테스크를 실행에서 제외하라는 옵션이다.
- test : src/test/java 경로에 있는 모든 유닛 테스트 코드를 실행하는 역할을 한다.
즉 -x test 는 test 를 제외하고 빌드를 실행하라는 명령어다.
- ./gradlew build: 컴파일 → 테스트 실행 → 패키징
- ./gradlew build -x test: 컴파일 → (테스트 건너뜀) → 패키징
(3) 컴파일
- 다운로드한 라이브러리와 src/main/java 안의 .java 파일(소스 코드)을 읽는다.
- 컴퓨터(JVM)가 알아들을 수 있는 .class 파일(JVM 바이트코드)로 번역한다.
(이 결과물은 build/classes/java/main, 리소스는 build/resources/main 에 저장된다.) - 외부 라이브러리(의존성 .jar 파일)들은 이미 이미 컴파일을 완료한 .class 파일들의 묶음이다. 따라서 Gradle은 이 파일들을 다시 컴파일하지 않는다.
- 대신 .java 코드를 컴파일할 때 참조용으로 사용된다.
- 예를 들어, User.java를 작성할 때 스프링의 @Entity 어노테이션을 썼다면, 컴파일러가 스프링 라이브러리 파일(이미 컴파일된)을 참고하여 보는 것과 같다.
- Resources 안의 설정 파일(.yml, .properties, .html 등): 이 파일들은 프로그래밍 코드가 아니다. 그냥 텍스트 파일이나 데이터 파일이다. 따라서 컴파일할 필요가 없다.
- Gradle은 processResources라는 태스크를 실행하여 src/main/resources에 있는 모든 파일을 build/resources/main 디렉토리로 그대로 복사한다.
- application.yml과 같은 설정 파일들은 프로그램이 시작될 때 YamlPropertySourceLoader가 SnakeYAML을 사용해 읽어들인 뒤, Environment/Binder를 통해 @ConfigurationProperties 등에 바인딩됩니다.
(4) 패키징(압축)
Spring Boot의 bootJar인 경우 (2)(3)과 리소스를 하나의 실행형 JAR로 묶고, 일반 jar 인 경우 의존성은 포함되지 않는다.
- (2)에서 다운로드한 라이브러리 파일(.jar)
- (3)에서 컴파일한 내 코드 파일(.class)
- src/main/resources 안의 설정 파일(.yml 등)
- 이 모든 것을 하나의 큰 .jar 파일(ex: my-app-0.0.1-SNAPSHOT.jar)로 압축한다
(이 파일은 build/libs 폴더에 저장된다.)
5) gradle-wrapper.jar 파일이 없다 (트러블슈팅)

위의 사진을 보면 oneco/gradle/wrapper/gradle-wrapper.jar 파일이 없다는 것을 알 수 있다.
gradle-wrapper.jar 파일은 무엇일까?
- 이것은 실행 가능한 Java 프로그램이다. (JAR는 "Java Archive"의 약자이며, 컴파일된 Java 코드와 리소스를 담고 있는 압축 파일이다.)
- 역할 (HOW): 이 파일은 '부트스트래퍼(Bootstrapper)' 역할을 한다. 사용자가 터미널에서 gradlew (또는 gradlew.bat) 스크립트를 실행하면, 이 스크립트는 실제로 java -jar gradle-wrapper.jar ... 명령을 내부적으로 실행하여 이 Java 프로그램을 깨운다.
- gradle-wrapper.jar 작동 순서:
- gradlew 스크립트에 의해 실행된다.
- 실행 즉시, gradle-wrapper.properties 설정 파일을 찾아 읽는다.
- .properties 파일에 지정된 Gradle 버전을 사용자의 로컬 캐시(보통 ~/.gradle/)에서 찾는다.
- 만약 캐시에 해당 버전이 없으면, .properties 파일의 distributionUrl에 적힌 주소에서 해당 Gradle 버전(예: gradle-8.5-bin.zip)을 다운로드한다.
- 다운로드한 .zip 파일의 압축을 풀고, 실제 Gradle 실행 파일을 사용해 사용자가 요청한 작업(예: build, test)을 수행하도록 위임(delegate)한다.
결론: gradle-wrapper.jar는 Gradle을 다운로드하고, 압축 풀고, 실행하는 '실제 로직'을 담고 있는 작은 실행 프로그램이다. 이 파일이 없으면, gradlew 스크립트는 실행할 프로그램 본체가 없기 때문에 Unable to access jarfile 오류를 낸다.
gradle-wrapper.properties 파일은 무엇일까?
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
- 이것은 단순한 텍스트 기반의 설정 파일이다. Java의 표준 속성 파일 형식(Key=Value)을 따른다.
- 역할 (WHAT): 이 파일은 gradle-wrapper.jar 프로그램에게 '무엇을, 어디서, 어떻게' 가져와야 하는지 '지시 사항'을 전달한다.
- 주요 설정값:
- distributionUrl: 다운로드할 Gradle 배포판(.zip 파일)의 정확한 URL 주소이다. 이 주소를 통해 5.0, 7.2, 8.5 등 원하는 특정 버전을 지정할 수 있다.
- distributionSha256Sum: 다운로드한 .zip 파일이 중간에 변조되지 않았는지, 혹은 손상되지 않았는지 확인하기 위한 보안용 체크섬(Checksum) 값이다. (현재 예시에는 보이진 않지만, 이 필드가 들어갈 수 있다는 의미로 이해)
- zipStorePath / distributionPath: 다운로드한 .zip 파일과 압축 해제된 Gradle 파일을 어디에 저장할지 경로를 지정한다.
결론: gradle-wrapper.properties는 gradle-wrapper.jar라는 프로그램이 실행될 때 참조하는 설정값(매개변수)이다.
왜 두 파일이 모두 필요한가?
이 둘을 분리하는 것은, 관심사의 분리 원칙을 따른다.
- 만약 .jar 파일만 있다면?
Gradle 버전을 업그레이드(예: 8.5 → 8.6)하고 싶을 때마다, 8.6 버전을 다운로드 하도록 하드코딩된 새로운 gradle-wrapper.jar 파일을 통째로 교체해야 한다. 이는 매우 번거롭고 비효율적이다. - 만약 .properties 파일만 있다면?
설정 정보는 있지만, 이 distributionUrl을 읽고, HTTP로 파일을 다운로드하고, 체크섬을 검사하고, 압축을 해제하고, 실행하는 실제 프로그램 로직이 없다.
따라서 gradle-wrapper.jar은 어떻게 다운로드하고 실행할지를 담당하고 gradle-wrapper.properties는 무엇을 다운로드할지를 담당한다.
6) gradle wrapper 명령어 실행 오류 (전역 gradle 버전 문제)
gradle wrapper 실행 (오류 발생)
FAILURE: Build failed with an exception.
* What went wrong:
'org.gradle.api.tasks.TaskProvider org.gradle.api.tasks.TaskContainer.named(java.lang.String, java.lang.Class)'
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 2s
gradle-wrapper.properties는 이미 8.14로 되어 있음
그런데 로컬에서 gradle wrapper 명령어를 치면 위의 What went wrong:안의 오류가 발생
→ 이건 Gradle 버전이랑 플러그인 코드가 서로 안 맞아서 생기는 NoSuchMethodError 계열 에러
왜 이런 에러가 뜨는가
- gradle wrapper를 실행하면 지금 PC에 설치된 전역 gradle이 실행됨
(./gradlew 가 아니라 gradle 명령)
- 그 Gradle이 build.gradle/ settings.gradle 을 읽으면서 Spring Boot Gradle Plugin이나 다른 플러그인을 로딩함
- 그런데 그 플러그인 코드 안에서 tasks.named(…) 코드를 쓰고 있는데 내 전역 Gradle 버전에는 그 메서드가 없음
즉, gradle-wrapper.properties 가 8.14인 것과 내가 지금 실행 중인 gradle 버전은 별개다.
그래서 먼저 현재 gradle 버전을 체크해봤다.
gradle -v
결과:
------------------------------------------------------------
Gradle 4.4.1
------------------------------------------------------------
즉, 전역 gradle이 너무 예전꺼여서 wrapper도 못 만드는 상태이다.
7) 해결 방법 (zip/unzip → SDKMAN → gradle 설치 → wrapper 생성)
1단계: zip/ unzip 설치
sudo apt update
sudo apt install zip unzip -y
2단계: SDKMAN 설치
SDKMAN은 여러 버전의 소프트웨어 개발 키트(SDK)를 쉽게 설치하고 관리하며, 버전 간 전환을 도와주는 도구이다.
Gradle, Java, Maven, Kotlin 등 **여러 버전의 개발 도구(SDK)를 설치하고, 이 버전들 사이를 손쉽게 오갈 수 있도록 관리해주는 '버전 관리 매니저'**이다.
curl -s "https://get.sdkman.io" | bash
3단계: SDKMAN 활성화
source "$HOME/.sdkman/bin/sdkman-init.sh"
- 명령어 분석
단순히 sdk라는 프로그램을 실행하는 것이 아니라, SDKMAN이 동작하는 데 필요한 환경 설정을 현재 터미널에 불러오는(loading) 과정이다.
sdkman-init.sh 스크립트가 구체적으로 하는 일은 다음과 같다.
- sdk 명령어를 등록한다:
사실 sdk는 단순한 실행 파일이 아니라, 쉘 함수(function)이다. sdk install ..., sdk use ... 같은 복잡한 작업을 수행하려면 이 함수가 현재 터미널에 등록되어야 한다. - 환경 변수(PATH)를 설정한다:
SDKMAN의 핵심 기능은 sdk use java 17처럼 버전을 바꿀 때마다 PATH 환경 변수를 즉시 변경해 주는 것이다. 이 init.sh 스크립트가 PATH를 관리하는 초기 설정을 수행한다.
source를 쓰는 이유
컴퓨터에서 스크립트를 실행하는 방법은 크게 두 가지가 있다.
- 그냥 실행하기 (./script.sh):
스크립트가 새로운 자식 쉘에서 실행되고, 실행이 끝나면 그 쉘은 닫힌다. 스크립트가 PATH를 변경했더라도, 그 변경 사항은 닫힌 쉘과 함께 사라진다. 현재 내가 사용 중인 터미널에는 아무 영향이 없다. - source로 실행하기 (source ./script.sh):
스크립트가 현재 쉘에서 직접 실행된다. 스크립트가 PATH를 변경하거나 sdk 함수를 등록하면, 그 내용이 현재 터미널 세션에 그대로 남게 된다.
결론: source "$HOME/.sdkman/bin/sdkman-init.sh"는 SDKMAN이 내 터미널 환경을 직접 제어할 수 있도록(함수를 등록하고 PATH를 바꿀 수 있도록) 허가하고 초기화하는 필수 명령어이다.
이 명령어는 보통 .bashrc나 .zshrc 같은 쉘 설정 파일에 추가해 둔다. 그래야 새 터미널을 열 때마다 자동으로 SDKMAN이 활성화된다.
4단계: Gradle 설치
sdk install gradle 8.14.3
설치 후 버전 확인
gradle -v
결과
------------------------------------------------------------
Gradle 8.14.3
------------------------------------------------------------
5단계: wrapper 생성
cd /mnt/c/Users/gimin/IdeaProjects/Backend/oneco
gradle wrapper --gradle-version 8.14.3
6단계: 생성됐는지 확인
ls gradle/wrapper
gradle-wrapper.jar
gradle-wrapper.properties
7단계: 실패했던 부분 로컬에서 build 해보기
./gradlew --version
./gradlew clean build -x test
8) 7-a : 자바 버전 문제 (toolchain 17)
Cannot find a Java installation ... matching: {languageVersion=17, ...}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
build.gradle에 이런 코드가 있다
→ 즉 프로젝트는 JDK17로 컴파일해달라고 설정되어 있다.
그런데 지금 WSL에는 JDK 21만 있고, 17이 설치되어 있지 않아서 Gradle이 찾을 수 없다고 죽은 상황이다.
CI(Github Actios)에서는 Set up JDK 17 해줘서 괜찮고, 지금 죽는 건 로컬 빌드에서 JDK 17이 없다고 뜬다.
따라서 WSL에 JDK 17을 설치했다.
sdk install java 17.0.13-tem
ls ~/.sdkman/candidates/java
17.0.13-tem current
그리고 다시 빌드한 결과 성공했다.
./gradlew clean build -x test
[결과]
BUILD SUCCESSFUL in 1m 16s
프로젝트에서는 잘 돌아갔는데 빌드에서 문제가 생긴 이유
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
“이 프로젝트는 JDK 17로 컴파일해줘” 라는 약속.
- CI(GitHub Actions)에서는 우리가 actions/setup-java@v4로 JDK 17을 깔아주고 있으니까 문제 없음.
(현재 워크플로우는 v3지만, “CI에서 setup-java로 JDK17을 깔아준다”는 결론은 동일)(정리) - IntelliJ에서 실행할 때는, 윈도우 쪽 JDK 17(예: C:\Program Files\...)을 써서 잘 돌아갔을 가능성이 큼.
- 근데 지금은 WSL(Ubuntu) 안에서 ./gradlew를 돌리는 중이고,
거기에는 java -version이 21.0.8 하나만 있어서 17이 없음.
Intellij Run/Debug로 실행
→ 이건 WSL 말고 윈도우 JVM(Intellij 설정에 지정된 JDK)을 사용해서 돌린다.
[CI/CD 1편] GitHub Actions + DockerHub + SSM으로 EC2 자동 배포 CI/CD 큰그림
CI/CD를 깊게 다룬 계기과거 프로젝트에서는 CI/CD가 너무 어렵게 느껴져서, 제대로 이해하지 못한 채 GPT가 제공한 워크플로우를 그대로 복사·붙여넣기 하며 구축했다. 하지만 이번 프로젝트에서
gimini.tistory.com
[CI/CD 2편] CI 빌드 파트 완전 해부: JDK 17 세팅 → Gradle 캐시 → Gradle Wrapper → build -x test ( + 트러블
이 글은 1편에서 잡은 큰그림 중, GitHub Actions의 build job을 “Runner 내부에서 실제로 어떤 일이 벌어지는지” 기준으로 풀어낸다.이번 2편에서 다루는 범위는 아래와 같다.Set up JDK 17Gradle Caching (actio
gimini.tistory.com
[CI/CD 3편] Docker 이미지 빌드/푸시 전략 완전 해부: Login → metadata(latest+sha) → buildx 캐시 → DockerHub
[CI/CD 3편] Docker 이미지 빌드/푸시 전략 완전 해부: Login → metadata(latest+sha) → buildx 캐시 → DockerHub push
gimini.tistory.com
[CI/CD 4편] SSH 없이 배포하기: GitHub Actions → AWS Credentials → SSM send-command → EC2 deploy.sh, 그리고 시
[CI/CD 4편] SSH 없이 배포하기: GitHub Actions → AWS Credentials → SSM send-command → EC2 deploy.sh, 그리고 시크릿 주입
gimini.tistory.com