[백엔드] gradle init(cli) 및 Spring Initializer curl 등을 통한 구성 시 필요한 부가적인 프로젝트 구성 관련 개발지식

Hyo Kyun Lee·2025년 10월 29일
0

백엔드

목록 보기
29/29

1. 개요

이번에 Spring Framework와 Spring Boot 프로젝트를 모두 직접 구성하고 두 프로젝트의 구현과정을 비교하는 작업을 하였는데, 구현 이전에 프로젝트 구성을 할 때부터 곤란한 상황들을 많이 직면하였다.

그런데 단순히 기존 프로젝트 구성 과정에서 누락한 부분이나 추가적으로 필요한 부분 등의 조치로 해결한 경험이 아닌, cli를 통한 gradle init 및 Spring Initializer curl 등 기존 구성과정과 완전히 다른 방식으로 시도하고 관련 문제를 해결한 경험을 새롭게 장착하게 되어 그 과정을 기록하게 되었다.

단순히 프로젝트를 git clone하거나 Spring initializer 홈페이지에 들어가서 폴더를 다운로드하는 것은 너무나 잘 알려진 과정인데, 이와 다른 방법으로 프로젝트 구성을 시도하였고 이 과정에서 새롭게 알게된 프로젝트 구성 관련 개발지식이 너무나 알차게 느껴졌기에 글을 남긴다.

각 두가지 방법을 통해 프로젝트를 구성하고, 이 과정에서 공통적으로 발생한 문제점과 각각의 대응과정을 단계적으로 살펴보겠다.

2. 일반적인 방법 외 2가지 프로젝트 구성 방법

기본적으로 Web을 통해, 구체적으로 Spring Initializer 및 git clone 등을 통해 프로젝트 "폴더"를 UI적으로 확인하면서 구성할 수 있겠다.

물론 이 방법이 가장 보편적이고 익숙하기에 굳이 다른 방법을 쓰지 않고도 편하게 프로젝트를 구성할 수 있겠지만, 다른 방법을 통해서도 구성이 가능하다.

오히려 명령어 한 줄이면 기본적인 Framework, boot 프로젝트 구성을 완료할 수 있기에, 더 편하게 구성할 수 있는 방법이기도 하다.

2-1. gradle init

gradle을 통해 Framework 프로젝트를 생성할 수 있다.

줄바꿈 용어가 차이가 좀 있는데, mac의 경우 \를 사용하고 window의 경우 ^를 사용한다. 내 경우는 window 개발환경이므로 ^를 사용한다.

gradle init \\ 
--type java-application \\ 
--dsl groovy \\ 
--test-framework junit-jupiter \\ 
--java-version 17 \\ 
--package com.system.batch \\ 
--project-name kill-batch-system \\ 
--no-split-project

이 줄바꿈을 인식하지 못한다면 그냥 한줄로 cli 명령어를 입력해주면 된다.

gradle init --type java-application --dsl groovy --test-framework junit-jupiter --java-version 17 --package com.system.batch --project-name kill-batch-system --no-split-project

지금은 프로젝트 구성과정과 문제해결에 대한 내용이 중요하기에, 각 명령어들을 해석하지는 않겠다.

이 cli를 생성하면 루트프로젝트(src/main)에서 package를 상위와 같이 구성한 하나의 Spring Framework 프로젝트를 구성할 수 있다.

이를 도식화하면 아래와 같다.

kill-batch-system/
 ├─ build.gradle
 ├─ settings.gradle
 ├─ gradlew
 ├─ gradlew.bat
 ├─ gradle/
 │   └─ wrapper/
 └─ src/
     ├─ main/
     │   └─ java/
     │       └─ com/
     │           └─ system/
     │               └─ batch/
     │                   └─ App.java
     └─ test/
         └─ java/
             └─ com/
                 └─ system/
                     └─ batch/
                         └─ AppTest.java

이에 대한 실제 프로젝트 모습을 보면 아래와 같다.

build.gradle를 살펴보면

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'java'
    id 'application'
}

의 내용이 들어가있는데, boot starter가 아니다.

전형적인 Spring Framework의 모습이다.

2-1-1. IntelliJ의 내장 JDK를 활용하여 내부터미널을 통한 Spring Framework 프로젝트 구성하기

이 경우 개발자 환경에 따라 많은 차이가 있을 수 있겠지만, 나의 경우 최대한 전역적인 gradle/jdk 등의 설치나 환경변수 설정을 지양하고자 하였다.

일단 Eclipse면 모르겠는데, 지금의 IntelliJ로는 각 프로젝트를 별도로 관리할 수 있고 심지어 JDK도 전역적인 환경변수가 아닌 지역적 변수 관리가 가능하다.

또한 내장하고 있는 jdk를 sdk로 활용하기에, 굳이 전역적인 jdk 설치 필요없이 내장 jdk와 이를 기반으로 한 터미널을 활용한다면 충분히 프로젝트 관리가 가능하다고 생각하였다.

이를 위해 일단 gradle은 OS범위에서 설치해주고, jdk는 내장 jdk를 사용하기 위해 IDE 내장 터미널에서 각각의 모듈 경로를 환경변수로 등록해주었다.

$env:PATH = "C:\gradle-9.1.0\bin;C:\Program Files\JetBrains\IntelliJ IDEA 2025.1.2\jbr\bin;$env:PATH"

참고로 내장 jdk는 ide를 설치한 경로 중 jbr에 있는 패키지 그 자체(젯브레인에서 제공하는 open jdk)이기에, 이를 그대로 사용하여 환경변수에 등록해주었다.

저 환경변수를 내장 터미널에 입력해주면, IDE 자체적으로 가지고 있는 환경변수(문자열을 concat하여 붙인 형태)에 해당 환경변수가 들어가고 이후 gradle/java 명령어를 칠때마다 해당 환경변수 문자열을 탐색하고 찾아서 이용하게 된다.

참고로 이를 프로젝트를 켤때마다 입력해주어야 하는데, 전역 설치보다는 비용적 소모가 훨씬 덜하다고 판단하였다.

2-2. Spring Initializer curl

Spring Initializer를 홈페이지에 들어가서 직접 구성하는 대신, curl로 작성하여 Boot 프로젝트를 만들 수도 있다.

curl 'https://start.spring.io/starter.tgz' \
    -d type=gradle-project \
    -d language=java \
    -d bootVersion=3.4.7 \
    -d groupId=com.system.batch \
    -d artifactId=kill-batch-system \
    -d name=kill-batch-system \
    -d packageName=com.system.batch \
    -d packaging=jar \
    -d javaVersion=17 \
    -d dependencies=batch,h2 \
    | tar -xzvf -

위 명령어는 mac 전용인데, 줄바꿈 표시를 ^로 바꾸거나 한줄로 바꿔서 cli에 입력해준다면 Spring Boot 프로젝트를 생성해줄 수 있다.

다만, 윈도우의 경우 이 명령어들을 문자열로 인식하기 때문에, curl의 정보들을 먼저 바이너리 파일로 받아오고 이를 tgz파일로 압축하여, 이를 tar로 압축을 해제하는 과정이 필요하다.

이 경우, 아래와 같이 프로젝트를 구성할 수 있다.

어노테이션부터 Boot 프로젝트임을 유추할 수 있는데, build.gradle을 살펴보면

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.7'
	id 'io.spring.dependency-management' version '1.1.7'
}

이와 같이 부트 프로젝트로 생성되었음을 알 수 있다.

위와 마찬가지로 내장 터미널과 모듈들을 이용해서도 충분히 구성할 수 있기에, 별도 전역적인 모듈설치 없이 진행하였다.

3. 프로젝트 인식 문제 발생

하지만 두 프로젝트 모두 프로젝트 인식 문제가 발생하였는데, 아래와 같이 기본적인 예약어들도 인식을 못하고 있었고 자바 클래스도 인식을 못하고 있었다.

일단 이 문제는 두가지로 볼 수 있다.

  • 이 프로젝트를 gradle 프로젝트로 인식을 하지 못하고 있다.
  • 이 프로젝트를 Spring 프로젝트로 인식을 하지 못하고 있다.

그런데, jdk를 인식하지 못하고 있기에 두번째 문제로 볼 수 있겠지만 이 jdk를 인식하기위해 gradle 프로젝트로 인식하는 것이 전제되어야하므로 두가지 문제를 모두 고려하여 문제를 해결해보았다.

참고로 이에 대한 문제 해결과정은 두 Framework, Boot 프로젝트 모두 동일하게 적용하여 해결하였으므로 나누지 않고 일괄적인 해결방안으로 기록하고자 한다.

4. 프로젝트 인식 문제 해결 과정

두가지 단계로 나누어 해결한다.

해결과정을 기술하기 전에, 먼저 이 과정을 진행하고 느낀 점을 아래와 같이 정리해보았다.

“JDK는 이미 IDE 내장으로 존재하지만,
이 프로젝트가 그걸 ‘어떻게 사용할지’에 대한 메타정보가 없기 때문에 IDE는 해당 JDK의 모듈을 classpath에 제대로 연결하지 못했다.

Gradle link를 통해 Gradle이 제공하는 빌드 스크립트 및 설정(classpath 정보)을 IDE가 읽게 되면서 IDE는 ‘이 프로젝트는 이 JDK를 이런 방식으로 사용할 것이다’라는 실행설명서를 얻게 되었고, 그 결과 JDK 내부 모듈과 외부 의존성(gradle 자체적인 의존성)을 classpath에 탑재해 비로소 코드 해석(모듈 인식)이 가능해졌다.”

4-1. gradle 인식

jdk를 인식하기 위해선, JVM이 이를 인식하기 위한 라이브러리들(jar)이 어디에 있는지 매핑해주어야 하는데, 프로젝트 별로 어떠한 의존성이 있고 jdk 버전이 있는지 모두 다르기 때문에, 적절하게 매핑해주는 작업을 선행해주어야 한다.

따라서 이 jdk를 인식할 수 있는 방법을 gradle에게 알려주는(gradle 자체적인 classpath를 등록), 즉 이 프로젝트가 gradle 프로젝트임을 먼저 인지시켜주어야 한다.

이 작업은 gradle import 혹은 link gradle을 통해 비교적 간단하게 해결할 수 있다.

참고로, build.grade, settings.gradle 모두 link gradle이 있는지 먼저 확인하고 gradle linking부터 먼저 작업하는 것이 좋겠다.

String과 같이 기본적인 jdk도 인식하지 못한다면 Spring 프로젝트로 인식 못했을 가능성이 크고, 어노테이션과 같은 의존성 부분을 인식하지 못한다면 gradle 프로젝트로 인식 못했을 가능성이 크다.

4-1-1. Spring Framework와 Boot의 gradle 인식차이 - gradle script 추가

다만, Spring Framework의 gradle(Wrapper)은 프로젝트를 빌드하기 위해 특정 task를 실행하는데, boot와 달리 해당 task가 없어서 실행이 되지 않는다면 build.gradle에 그 task를 직접 기재해주고 메뉴얼화해야 한다.

나의 경우, 아래와 같이 wrapper task가 gradle에 존재하지 않아 직접 build.gradle에 명기해주어야 하는 상황이었다.

Task 'wrapper' not found in project ':app'. * Try: > Run gradle tasks to get a list of available tasks. > For more on name expansion, please refer to https://docs.gradle.org/9.1.0/userguide/command_line_interface.html#sec:name_abbreviation in the Gradle documentation. > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to generate a Build Scan (Powered by Develocity). > Get more help at https://help.gradle.org. BUILD FAILED in 34s Configuration cache entry stored.

따라서 build.gradle에 아래 task를 추가해주었고 정상적인 프로젝트 빌드를 할 수 있었다.

tasks.register('wrapper', Wrapper)

참고로, 이 gradle task를 등록해주었다면

gradle wrapper

이 cli 명령어를 진행할 수 있게 되며, 이를 통해 프로젝트 버전에 맞는 gradle Wrapper 파일을 생성하고 cli로 ./gradlew, gradlew.bat의 명령어를 사용할 수 있게 된다.

프로젝트 버전에 맞는 gradle Wrppaer가 생성되었다면,

./gradlew dependencies

을 통해 현재 프로젝트 상황에 맞는 gradle 의존성을 그대로 다운로드 받는다. 내장해서 사용할 수 있도록 외부 라이브러리 의존성을 주입받는 단계라 보면 된다.

여기까지 하였다면 안정적으로 프로젝트를 빌드할 수 있는 단계까지 진행한 것이지만, 보통은 gradle wrapper까지하면 프로젝트 빌드를 할 수 있게 된다(gradlew는 의존성 초기화 후 다시 다운로드 하는 작업).

Framework는 이렇듯 gradle Wrapper를 여기에 맞게 생성해주어야 하는데, boot는 모든 것을 자동으로 생성하여준다(link만 해도 해결가능).

4-2. jdk 인식(Framework)

보통 gradle 프로젝트 인식이 되면 jdk를 인식할 수 있기에 프로젝트 구성은 끝낼 수 있는데, 지금의 문제처럼 gradle을 인식시켜주었음에도 jdk를 인식하지 못하는 문제가 발생할 수 있다.

Boot는 사실 이전단계에서 모두 해결하였는데, Framework 쪽이 문제였다.

여기서 Boot가 얼마나 편한지 한번 더 느낄 수 있었는데, Framework 경우 이 프로젝트가 Spring 프로젝트라는 것을 인식시켜주고 더불어 소스루트가 어디인지 직접 인식시켜주는 작업이 추가적으로 필요하다.

이 작업은 그나마 간단하다.

Spring에서 인식해주는 루트소스를 직접 명시해주면 된다.

src/main/java
src/main/resources
src/test/java

이렇게 구조가 잘 나뉘어져있어도, gradle이 src/main/java를 소스루트로 인식해야 비로소 Spring 프레임워크로 인식하고 jdk까지 온전히 인식이 가능해진다.

위와 같이 src/main/java를 mark as source root로 해줌으로써 비로소 모든 프로젝트 인식 작업을 완료할 수 있었다.

이 과정으로도 프로젝트가 불안하다면, Spring boot starter web 등, implementation 의존성을 직접 추가해주면 된다.

5. 결론

이번 인식문제를 해결하면서 느낀점은 프로젝트를 구성하는 방법은 매우 다양하게 있으며, 기존에 편하게 느꼈던 방법들이 오히려 효율성이 불리할 수 있다는 점을 느낄 수 있었다.

이번에 프로젝트 구성을 새롭게 시도해보고 여러 문제를 직면하고 해결하면서 프로젝트를 구성하는 여러 원리나 개발지식, 노하우들을 새로 알 수 있었다.

물론 세부적인 cli는 좀 더 알아봐야 하겠으나, 이번 시도를 통해 많은 것을 느끼고 배울 수 있었던 기회가 되었던 것 같다.

0개의 댓글