/개발/Maven "Could not resolve dependencies" 빌드 실패 원인 6가지·settings.xml 해결법
개발Maven 빌드오류Could not resolve dependencies

Maven "Could not resolve dependencies" 빌드 실패 원인 6가지·settings.xml 해결법

Maven "Could not resolve dependencies"·"Failed to execute goal" 에러 해결. 사내 Nexus 미러·프록시·PKIX·.m2 캐시 손상 등 원인 6가지를 복붙 명령어와 settings.xml 예제로 5분 안에 진단하세요.

Maven "Could not resolve dependencies" 빌드 실패 원인 6가지·settings.xml 해결법

Maven "Could not resolve dependencies" 빌드 실패 원인 6가지와 settings.xml 해결법

어제까지 잘 되던 빌드가 갑자기 깨졌다면

콘솔에 빨갛게 찍힌 Failed to execute goal ... Could not resolve dependencies 한 줄을 그대로 복사해서 검색하다 들어오셨을 겁니다. 결론부터 말하면, 이 에러 메시지 자체는 "의존성을 못 가져왔다"는 결과만 알려줄 뿐 진짜 원인은 그 아래 몇 줄에 숨어 있습니다.

특히 사내 Nexus/Artifactory, 프록시, 사설 CA가 깔린 폐쇄망 환경에서는 코드 한 줄 안 건드렸는데도 빌드가 깨지는 일이 잦습니다. 이 글은 Maven 전용으로, settings.xml의 mirror/proxy/server 태그와 ~/.m2 구조, mvn 플래그만으로 원인을 5분 안에 특정하고 복구하는 데 집중합니다.

이 글의 범위: Maven CLI / settings.xml / 로컬 저장소(.m2) 중심. IDE 내장 빌드나 다른 빌드 도구 이슈는 다루지 않습니다.

먼저 에러를 정확히 읽기 — mvn -X로 진짜 원인 찾기

가장 흔한 실수가 맨 마지막 Failed to execute goal 줄만 보고 좌절하는 것입니다. 이 줄은 항상 똑같이 나옵니다. 우리가 봐야 할 건 그보다 위쪽의 Caused by 또는 Could not transfer artifact 줄입니다.

디버그 로그를 켜고 다시 돌려보세요.

Bash
# 전체 통신/저장소 접근 로그까지 출력
mvn -X clean install

mvn -X를 켜면 어느 저장소(repository)로 어떤 URL에 접속을 시도했고, 무슨 이유로 실패했는지가 그대로 찍힙니다. 아래 키워드 중 어느 게 보이는지가 분기점입니다.

로그에서 보이는 문구매핑되는 원인한 줄 의미
Could not transfer artifact ... Connection refused / timed out② 프록시·방화벽 / ① 미러 주소 오타저장소에 아예 못 닿음
PKIX path building failed / unable to find valid certification path③ 사설 인증서 SSL 실패TLS 핸드셰이크 거부
Could not find artifact ...:jar:1.2.3 in central (404)⑤ 버전 오류·SNAPSHOT 미배포저장소엔 닿았지만 그 버전이 없음
Failed to read artifact descriptor / .lastUpdated 언급④ 로컬 캐시 손상망가진 캐시를 붙잡고 있음
Cannot access ... in offline mode-o 플래그 오용오프라인인데 새 의존성 요구

충돌이 의심되면 의존성 트리부터 확인합니다.

Bash
# 전체 의존성 트리
mvn dependency:tree

# 특정 라이브러리가 어디서 끌려오는지, 버전 충돌이 어떻게 정리됐는지 추적
mvn dependency:tree -Dverbose -Dincludes=org.springframework:spring-core

원인 ① 사내 Nexus/Artifactory 미러·settings.xml 오설정

Connection refused인데 URL이 Maven Central이 아니라 사내 Nexus 주소(nexus.회사.co.kr)라면, 십중팔구 settings.xml의 미러 설정 문제입니다. Nexus가 점검 중이거나, 주소가 바뀌었거나, 인증 정보가 만료됐을 때 발생합니다.

~/.m2/settings.xml을 열어 <mirrorOf>가 무엇을 가리키는지부터 확인하세요. central만 미러하는지, *(전부)인지에 따라 동작이 완전히 달라집니다.

XML
<!-- ~/.m2/settings.xml : 사내 Nexus 미러 + 인증 예제 -->
<settings>
  <servers>
    <!-- mirror의 id와 반드시 동일해야 인증이 적용됨 -->
    <server>
      <id>company-nexus</id>
      <username>${env.NEXUS_USER}</username>  <!-- 평문 대신 환경변수 권장 -->
      <password>${env.NEXUS_PASS}</password>
    </server>
  </servers>

  <mirrors>
    <mirror>
      <id>company-nexus</id>           <!-- server의 id와 일치 -->
      <name>Company Nexus</name>
      <url>https://nexus.example.co.kr/repository/maven-public/</url>
      <mirrorOf>*</mirrorOf>           <!-- 모든 저장소 요청을 Nexus로 우회 -->
    </mirror>
  </mirrors>
</settings>

핵심은 <server>의 id와 <mirror>의 id가 정확히 같아야 인증 헤더가 붙는다는 점입니다. 401/403이 뜬다면 이 id 불일치나 비밀번호 만료를 먼저 의심하세요.

원인 ② 회사 프록시/방화벽으로 Maven Central 차단

<mirrorOf>central만이라 Maven Central로 직접 나가야 하는데 Connection timed out이 뜬다면, 회사 프록시를 안 거쳐서 방화벽에 막힌 경우입니다. 먼저 차단 여부를 진단합니다.

Bash
# 프록시 없이 직접 닿는지 확인 (timeout이면 차단)
curl -I https://repo.maven.apache.org/maven2/

# 프록시를 거치면 되는지 확인
curl -I -x http://proxy.example.co.kr:8080 https://repo.maven.apache.org/maven2/

프록시 경유 시 200이 뜨면 settings.xml에 프록시를 등록합니다.

XML
<!-- ~/.m2/settings.xml : 프록시 설정 예제 -->
<settings>
  <proxies>
    <proxy>
      <id>company-proxy</id>
      <active>true</active>             <!-- false면 무시됨, 켜는 걸 잊지 말 것 -->
      <protocol>http</protocol>
      <host>proxy.example.co.kr</host>
      <port>8080</port>
      <username>proxyUser</username>    <!-- 인증 프록시일 때만 -->
      <password>proxyPass</password>
      <!-- 사내 Nexus는 프록시 제외(직접 접속) -->
      <nonProxyHosts>nexus.example.co.kr|localhost|127.0.0.1</nonProxyHosts>
    </proxy>
  </proxies>
</settings>

<nonProxyHosts>를 빠뜨리면 사내 Nexus까지 외부 프록시로 보내려다 또 실패하니 주의하세요. 참고로 Maven Central은 HTTP 접근을 막고 HTTPS만 허용하므로, 오래된 설정의 http://...central URL은 모두 https://로 바꿔야 합니다.

원인 ③ 사설 인증서로 인한 PKIX/SSL 핸드셰이크 실패

PKIX path building failed 또는 unable to find valid certification path to requested target가 보이면 SSL 신뢰 문제입니다. 사내 프록시/Nexus가 사설 CA로 서명한 인증서를 쓰는데, JDK의 신뢰 저장소(cacerts)에 그 CA가 없어서 거부되는 상황입니다. 제로트러스트·폐쇄망 도입이 늘면서 가장 빠르게 증가하는 케이스입니다.

핵심 해결은 사설 CA 인증서를 JDK에 임포트하는 것입니다.

Bash
# 사내 CA 인증서를 JDK 신뢰 저장소에 등록
keytool -import -trustcacerts -alias company-ca \
  -file company-ca.crt \
  -keystore "$JAVA_HOME/lib/security/cacerts" \
  -storepass changeit

PKIX 오류의 인증서 추출 방법, 체인 구성, JDK 버전별 경로 차이 등 상세 절차는 별도의 PKIX 트러블슈팅 글에서 깊게 다루므로 여기서는 핵심만 짚고 넘어갑니다.

원인 ④ 손상된 로컬 저장소 캐시(~/.m2/repository)

저장소엔 잘 닿는데 특정 라이브러리만 계속 실패하고 로그에 .lastUpdated가 보인다면, 다운로드가 중간에 끊겨 캐시가 망가진 경우입니다. Maven은 실패 기록(*.lastUpdated)을 남겨두고 한동안 재시도를 안 합니다.

전체 .m2를 통째로 지우기 전에, 실패 마커만 정리하는 게 훨씬 빠릅니다.

Bash
# 1) 실패 마커만 삭제 (가장 가볍고 안전)
find ~/.m2 -name "*.lastUpdated" -delete

# 2) 특정 의존성만 정리 후 재다운로드
mvn dependency:purge-local-repository -Dinclude=org.springframework:spring-core

# 3) 그래도 안 되면 강제 업데이트로 재시도
mvn -U clean install

전체 rm -rf ~/.m2/repository는 최후의 수단입니다. 수 GB를 다시 받아야 해서 CI에서는 특히 비효율적입니다.

원인 ⑤ 버전 충돌·존재하지 않는 버전·SNAPSHOT 미배포

Could not find artifact ...:jar:1.2.3 in central (404)처럼 404가 명확히 보이면 통신 문제가 아닙니다. 저장소엔 잘 닿았는데 그 좌표(groupId:artifactId:version)가 실제로 없는 것입니다. 흔한 3가지 원인은:

  • 오타나 잘못된 버전 번호 (예: 존재하지 않는 1.2.3)
  • 동료가 mvn install만 하고 Nexus에 deploy 안 한 SNAPSHOT 미배포
  • 다른 라이브러리가 끌어온 추이적(transitive) 의존성의 버전 충돌
Bash
# 어떤 라이브러리가 문제의 버전을 끌고 오는지 정확히 추적
mvn dependency:tree -Dverbose -Dincludes=com.example:problem-lib

Connection refused(②)와 404(⑤)를 구분하는 게 핵심입니다. 전자는 "문을 못 열었다", 후자는 "문은 열었는데 물건이 없다"입니다.

원인 ⑥ -U·-o 플래그 오용

마지막으로 의외로 자주 발목 잡는 게 플래그입니다.

  • -o(offline): 오프라인 모드. 새 의존성을 못 받는데 캐시에 없으면 Cannot access ... in offline mode가 납니다. 습관적으로 켜두지 마세요.
  • -U(update): SNAPSHOT을 강제로 다시 받습니다. CI에서 최신 SNAPSHOT을 보장할 때 유용하지만, 매 빌드마다 켜면 불필요한 네트워크 호출로 느려지고 간헐적 실패를 부릅니다.
Bash
# SNAPSHOT 최신본이 필요할 때만
mvn -U clean install

5분 진단 플로우차트

에러 문구만 들고 아래 순서대로 내려오면 자기 케이스를 찾을 수 있습니다.

  1. 로그에 PKIX 또는 valid certification path가 있는가? → 예: 원인 ③ (인증서 임포트)
  2. 404 또는 Could not find artifact가 있는가? → 예: 원인 ⑤ (버전·SNAPSHOT 확인)
  3. Connection refused / timed out이고 실패 URL이 사내 Nexus인가? → 예: 원인 ① (미러·인증 점검)
  4. Connection timed out이고 URL이 Maven Central인가? → 예: 원인 ② (프록시 설정)
  5. .lastUpdated 언급이 있거나 특정 1~2개만 실패하는가? → 예: 원인 ④ (find ~/.m2 -name "*.lastUpdated" -delete)
  6. offline mode 문구가 있는가? → 예: 원인 ⑥ (-o 제거)

위 6단계로 안 잡히면, settings.xml을 임시로 비활성화하고 순정 환경에서 한 번 돌려 환경 변수를 분리해 보세요.

실무에서 느낀 점

경험상 폐쇄망 프로젝트에서 신규 입사자 PC 빌드 실패의 80%는 ①과 ③ 두 가지였습니다. 그래서 저희 팀은 검증된 settings.xml과 사내 CA 인증서를 묶어 온보딩 스크립트로 자동 배포했더니 "빌드가 안 돼요" 문의가 거의 사라졌습니다. 개인이 매번 디버깅하는 것보다 표준 settings.xml을 형상관리하는 것이 훨씬 비용이 쌉니다.

재발 방지 체크리스트

  • settings.xml을 팀 표준으로 형상관리하고, 비밀번호는 환경변수로 분리
  • CI(GitHub Actions/Jenkins)에서 ~/.m2/repository를 캐시하되, *.lastUpdated는 캐시 키에서 제외
  • -U는 SNAPSHOT 갱신이 꼭 필요한 잡에서만, 일반 빌드는 끄기
  • 모든 저장소 URL을 https://로 통일 (Maven Central HTTP 차단 대응)
  • 사내 CA 인증서를 JDK 이미지/온보딩 스크립트에 미리 포함

자주 묻는 질문 (FAQ)

Q. Could not resolve dependenciesFailed to execute goal은 다른 에러인가요? A. 사실상 한 묶음입니다. Failed to execute goal은 플러그인 실행이 멈췄다는 상위 메시지이고, Could not resolve dependencies가 그 직접 원인입니다. 진짜 원인은 항상 그 아래 Caused byCould not transfer artifact 줄에서 확인하세요.

Q. ~/.m2/repository를 통째로 지워도 되나요? A. 됩니다만 최후의 수단입니다. 먼저 find ~/.m2 -name "*.lastUpdated" -delete로 실패 마커만 지우거나 mvn dependency:purge-local-repository로 문제 의존성만 정리하세요. 전체 삭제는 수 GB 재다운로드를 유발해 CI에서 특히 비효율적입니다.

Q. 404와 Connection refused는 어떻게 구분하나요? A. 404는 "저장소엔 닿았는데 그 버전이 없다"(원인 ⑤), Connection refused/timed out은 "저장소에 아예 못 닿았다"(원인 ①·②)입니다. 404면 버전·SNAPSHOT 배포 여부를, refused면 미러 주소와 프록시 설정을 점검하세요.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 15일

댓글

불러오는 중...