첫 실행과 경로
110분 안팎
학습 목표
JDK 17·Maven Wrapper·작업 경로와 종료 상태를 확인합니다.
개념
왜 실행 경로부터 확인하나요
동아리에서 보유 도서를 코드로 관리하려면 먼저 같은 소스로 같은 프로그램을 실행할 수 있어야 합니다. 첫 업무에서 흔한 막힘은 어려운 알고리즘보다 다른 폴더에서 명령을 실행하거나 다른 Java 버전으로 빌드하는 일입니다. 화면에 제목 한 줄이 나오는 것만으로 준비가 끝났다고 판단하지 않습니다. 어느 소스를 빌드했는지, 어떤 클래스를 실행했는지, 테스트가 실제로 실행되었는지를 함께 확인합니다. 이번 레슨은 한 권의 제목을 출력하는 가장 작은 프로그램을 사용해 그 확인 순서를 익힙니다.
소스, 클래스, 실행을 구분합니다
Java 소스는 사람이 수정하는 .java 파일입니다. javac는 소스를 컴파일해 JVM이 실행할 .class 파일을 만듭니다. java는 클래스를 실행합니다. 이 레슨의 CatalogApp.java에는 package lab 선언이 있으므로 전체 클래스 이름은 lab.CatalogApp입니다. 파일이 있는 경로와 클래스 이름은 서로 다른 개념입니다. 소스 파일은 src/main/java/lab/CatalogApp.java이고, Maven이 만든 클래스는 target/classes/lab/CatalogApp.class에 있습니다. 실행할 때는 java -cp target/classes lab.CatalogApp처럼 클래스 탐색 기준 경로와 전체 클래스 이름을 따로 전달합니다.
JDK는 컴파일 도구와 실행 환경을 제공합니다. java -version과 javac -version을 각각 확인해 주 버전이 17인지 봅니다. 패치 번호나 배포판 이름은 학습자마다 다를 수 있습니다. 둘 중 하나만 17이라고 다른 명령도 같은 설치를 쓴다고 단정할 수 없습니다. PATH가 여러 설치를 가리킬 수 있기 때문입니다. Maven도 ./mvnw -version에서 사용하는 Java를 확인합니다. 환경을 바꿨다면 터미널을 다시 열거나 JAVA_HOME과 PATH를 설치한 JDK 17 경로에 맞추고 세 명령을 다시 확인합니다.
프로젝트의 입구를 찾습니다
다운로드한 starter 압축을 풀고 pom.xml과 mvnw가 나란히 있는 폴더를 찾습니다. 이 폴더가 실습 루트입니다. macOS나 Linux 터미널에서 pwd는 현재 위치를, ls는 바로 아래 항목을 보여 줍니다. 경로에 공백이 있으면 cd 뒤의 경로를 따옴표로 감쌉니다. src/main/java는 실행 코드, src/test/java는 테스트 코드입니다. target은 빌드 산출물이므로 직접 고치는 곳이 아닙니다. 파일을 수정했는데 출력이 그대로라면 먼저 수정한 실습 폴더와 실행 중인 폴더가 같은지 확인합니다. starter와 solution을 동시에 열었을 때 특히 자주 생깁니다.
Maven Wrapper는 프로젝트가 정한 Maven을 실행하는 스크립트입니다. 이 실습에는 저장소의 mvnw와 .mvn/wrapper 파일이 포함되며 Maven 3.9.4를 사용합니다. 시스템에 mvn이 없어도 ./mvnw로 실행합니다. 다만 Wrapper가 JDK나 모든 의존성을 압축 안에 담는 것은 아닙니다. 처음 사용할 때 Maven 배포본과 의존성 다운로드가 필요할 수 있습니다. 준비된 캐시에서는 -o 옵션으로 오프라인 빌드를 합니다. 캐시가 비어 있는 본인 개발 환경에서는 네트워크가 가능할 때 ./mvnw test를 먼저 실행해 준비합니다.
main과 작은 출력 메서드를 읽습니다
package lab;
public class CatalogApp {
public static String label(String title) {
return "도서: " + title;
}
public static void main(String[] args) {
System.out.println(label(args.length == 0 ? "동아리 Java 노트" : args[0]));
}
}main은 프로그램의 시작점입니다. String[] args는 명령 뒤에 전달한 문자열 배열이며 args.length는 개수입니다. 인자가 없으면 기본 제목을, 있으면 첫 인자를 선택합니다. 조건 ? 앞의 표현 : 뒤의 표현은 조건에 따라 하나의 값을 고르는 문법입니다. public은 외부에서 사용할 수 있다는 표시, static은 객체를 만들지 않고 클래스에 속한 동작을 호출한다는 표시, void는 main이 반환값을 돌려주지 않는다는 표시입니다. 지금은 시작점의 형태를 그대로 사용하고 다음 레슨에서 반환값과 메서드 호출을 분리해 다룹니다.
label은 제목을 받아 출력용 문자열을 돌려줍니다. 문자열은 큰따옴표로 감싸고 +로 연결합니다. 이 레슨에서 빈 문자열의 결과는 접두사 뒤 공백까지만 있는 문자열입니다. 제목이 업무적으로 유효한지 판단하는 일은 아직 맡기지 않습니다. 출력 형식과 도서 생성 규칙을 구분해 두면 뒤에서 Book 검증을 추가해도 모든 출력 함수를 뒤집어 고칠 필요가 없습니다. 테스트의 기대값에 있는 공백도 계약의 일부이므로 임의로 지우지 않습니다.
오류의 단계와 종료 상태를 읽습니다
명령을 실행한 직후 echo $?로 종료 상태를 확인합니다. 일반적으로 0은 성공, 0이 아닌 값은 실패를 뜻합니다. 이 값은 바로 앞 명령의 상태이므로 다른 명령을 실행한 뒤 읽으면 다른 결과를 보게 됩니다. 테스트 실패를 화면의 빨간 글씨로만 판단하지 않고 종료 상태와 테스트 보고서를 함께 봅니다. 정상 실행에서도 출력이 없을 수 있습니다. 반대로 제목을 출력했어도 뒤에서 예외가 났다면 실행 전체는 실패할 수 있습니다. 출력과 성공 여부는 따로 확인할 자료입니다.
Could not find or load main class가 나오면 전체 클래스 이름, -cp 경로, target/classes 아래 클래스 존재 여부 순으로 확인합니다. lab/CatalogApp.class를 클래스 이름으로 전달하면 해결되지 않습니다. Permission denied가 mvnw 실행에서 나오면 실습 루트에서 chmod +x mvnw로 실행 권한을 줍니다. MissingProjectException 또는 no POM이라는 메시지는 pom.xml이 없는 위치에서 실행한 것일 수 있습니다. 파일을 새로 만들기 전에 pwd와 ls를 확인합니다. invalid target release: 17은 빌드에 선택된 JDK가 맞는지 살펴볼 단서입니다.
오프라인 빌드의 Cannot access ... in offline mode는 업무 코드가 틀렸다는 뜻이 아니라 필요한 의존성이 캐시에 없다는 뜻입니다. 이 경우 테스트 기대값을 수정하지 않습니다. JDK 확인, 실습 루트 확인, 의존성 준비를 마친 뒤 다시 실행합니다. 실습의 starter는 의도적으로 label 형식이 틀려 테스트가 실패합니다. 보고서에서 expected와 actual을 비교하고 CatalogApp.java의 반환식 한 곳을 고칩니다. 네 테스트가 실행되고 실패와 오류가 0인지 확인한 뒤 기본 제목과 직접 전달한 제목을 각각 출력해 봅니다.
레슨을 마친 증거
본인 메모에 JDK 주 버전, 실습 루트 확인 방법, 테스트 명령, 실행 명령을 남깁니다. 경로 전체를 남길 필요는 없지만 다른 사람이 같은 압축을 풀어 실행할 만큼 구체적으로 씁니다. JVM 내부 동작과 클래스 파일 버전의 자세한 설명은 더 읽기로 연결한 서재 장에서 이어 봅니다. 지금의 완료 기준은 소스 파일을 수정하고 다시 빌드한 뒤 올바른 클래스 이름으로 같은 결과를 재현하는 것입니다.
따라하기
JDK와 실습 루트 확인
starter 압축을 풀고 pom.xml과 mvnw가 나란히 있는 폴더로 이동합니다. 이 레슨의 모든 명령은 그 폴더에서 실행합니다. macOS/Linux 셸 기준입니다. 아래 주 버전이 17인지 확인합니다. 패치 번호와 배포판은 달라도 됩니다. mvnw 실행 권한이 없으면 chmod +x mvnw로 준비합니다.
java -version 2>&1
javac -version실행 결과
openjdk version "17.0.20.1" 2026-08-18 OpenJDK Runtime Environment Homebrew (build 17.0.20.1+0) OpenJDK 64-Bit Server VM Homebrew (build 17.0.20.1+0, mixed mode, sharing) javac 17.0.20.1
시작 코드의 TODO 수정
실습 루트의 src/main/java/lab/CatalogApp.java에서 label 반환식을 고칩니다. main은 유지합니다. 수정 전 ./mvnw test를 실행하면 일부 테스트가 실패합니다. 아래 조각은 해당 메서드나 선언 자리에 넣고, 이어 ./mvnw test로 컴파일과 테스트를 실행합니다. 의존성을 준비한 환경에서는 -o를 붙일 수 있습니다.
return "도서: " + title;정상과 경계를 직접 실행
앞 단계의 빌드가 끝나 target/classes가 만들어진 뒤 아래 명령을 실행합니다. 기본 제목과 직접 지정한 제목의 출력 접두사·공백을 비교합니다.
java -cp target/classes lab.CatalogApp
java -cp target/classes lab.CatalogApp "파일 도서 목록"실행 결과
도서: 동아리 Java 노트 도서: 파일 도서 목록
테스트와 종료 상태 확인
아래 명령으로 전체 테스트를 다시 실행하고 바로 이어 종료 상태를 읽습니다. -q는 정상 로그를 줄입니다. 아래 0은 echo가 보여 준 테스트 명령의 종료 상태입니다. target/surefire-reports의 XML 또는 txt에서 제공 테스트 4개가 실행되었고 실패·오류·건너뜀이 0인지 함께 확인합니다.
./mvnw -q test
echo $?실행 결과
0
확인 문제
실습
CatalogApp.label을 고쳐 "도서: "와 입력 제목을 정확히 이어 반환합니다. 한국어·영문·빈 문자열·양끝 공백 네 사례를 모두 통과시킵니다. label은 제목 유효성 검사 메서드가 아닙니다. 실습 루트에서 빌드하고 기본 제목과 인자로 전달한 제목을 실행합니다.
실행 명령
./mvnw test
기대 결과
제공 테스트 4개 실행, Failures 0, Errors 0, Skipped 0, 종료 코드 0입니다.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- JDK의 javac와 java는 각각 어떤 역할을 하나요?
- 클래스 경로와 패키지를 포함한 클래스 이름은 어떻게 구분하나요?