Java 개발을 하다 보면 여러 가지 Java 버전을 바꿔가며 사용해야 하는 경우가 있습니다.
이번 포스트는 Mac 환경에서 여러 개의 Java 버전을 바꿔가며 사용하는 방법을 설명하고자 합니다.
추가로, 필자는 주로 Windows 환경에서 개발을 하다 보니 Mac(혹은 Linux) 환경에서 개발환경을 구축하는 것이 매우 낯설었습니다.
단순히 Java 버전을 바꾸는 것임에도 그 과정에서 궁금한 점들이 많이 발생하였고, 이것저것 찾아보느라 단순히 Java 구성하는데도 시간이 오래 걸렸습니다.
처음 Mac에 Java 개발환경을 구축하려는 분들이 저처럼 잡다한(?) 궁금증 때문에 시간낭비를 하지 않았으면 하는 마음에 이번 포스트를 정리하여 올립니다.
혹시라도 글에 오류가 있다면 댓글로 바로잡아주시면 감사하겠습니다.
현재 필자의 OS는 Mac Catalina이며 쉘은 bash가 아닌 zsh를 사용 중입니다.
1. 설치된 Java 버전 확인하기
설치된 Java 버전을 확인하기 위해서는 터미널을 켜고 아래와 같은 명령어를 사용하면 됩니다.
$ /usr/libexec/java_home -V
저의 경우에는 openJDK 8과 openJDK 11 두 가지 버전이 설치되어 있는 것을 알 수 있습니다.
(OpenJDK 종류 및 JDK 설치하는 방법은 추후, 별도 포스트를 작성할 예정입니다.)
이 중 기본 JDK로 설정된 버전을 확인하기 위해선 아래와 같은 명령어를 사용하면 됩니다.
$ java -version
openJDK 11이 현재 기본 JDK 버전으로 설정되어 있는 것을 확인할 수 있습니다.
여기서 첫 번째 궁금증이 생겼습니다.
/usr/libexec/java_home는 무슨 커맨드인가?
1-Q1. /usr/libexec과 java_home
우선 /urs/libexec부터 알아보겠습니다.
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html
/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec.
Applications which use /usr/libexec in this way must not also use /usr/lib to store internal binaries, though they may use /usr/lib for the other purposes documented here.
Linux Foundation Reference 문서에 따르면 간단히 말해 /urs/libexec는 일종의 실행 파일이 모여있는 디렉터리인 것을 알 수 있습니다.
(유저나 쉘 스크립트에 의해 직접적으로 실행되지 않는 내부 바이너리라는 의미가 정확히 무엇인지는 모르겠네요...)
그럼 /urs/libexec에 어떤 파일이 있는지 조회해 보겠습니다.
$ ll /usr/libexec
/urs/libexec에는 많은 파일이 있으며, java_home 또한 해당 디렉터리에 존재하는 것을 알 수 있습니다.
그리고, java_home은 링크 파일로 실제 java_home파일은 /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java_home 위치에 있는 것을 확인할 수 있습니다.
(linux File 종류는 별도 포스트로 작성할 예정입니다.)
다음으로 java_home입니다. 아래 명령어를 통해 java_home의 사용법을 알 수 있습니다.
$ /usr/libexec/java_home -h
위 내용을 보면 java_home은 간단히 말해 현재 유저에 세팅된 Java와 jvm 정보를 조회하는 파일임을 알 수 있습니다.
사용법을 자세히 보면 아까 우리가 pc에 설치되어 있는 모든 Java 종류를 알기 위해 사용했던 명령어 ($ /usr/libexec/java_home -V)도 안내되어 있는 것을 알 수 있습니다.
여기서 우리가 조금 더 주의 깊게 봐야 하는 옵션은 -v 옵션입니다.
# openJDK 1.8 설치경로 출력
$ /usr/libexec/java_home -v 1.8
# openJDK 11 설치경로 출력
/usr/libexec/java_home -v 11
-v 옵션 뒤에 Java 버전(메이저 버전)을 명시할 경우, 해당 Java 버전이 설치된 경로를 필터 해 출력해주는 것을 알 수 있습니다.
우리는 이 기능을 이용하여, 기본 JDK 버전을 자유롭게 변경/설정하고자 합니다.
2. 기본 JDK 설정하기
기본 JDK를 설정하기 위해서는 windows에서 java를 설치하고 설정할 때와 마찬가지로 Mac 환경변수에 JAVA_HOME를 추가하고 PATH에 JAVA_HOME/bin 경로를 추가해야 합니다.
2.1 .zshrc에 JAVA_HOME 및 PATH 설정하기
필자는 zsh을 사용하므로 .zshrc파일에 JAVA_HOME 및 PATH를 설정해야 합니다.
(bash를 사용하시는 분들은 .bashrc 혹은 bash_profile을 수정하시면 됩니다.)
.zshrc을 아래와 같은 명령어를 통해 편집할 수 있습니다. (nano 편집기 사용)
$ nano ~/.zshrc
위 화면은 .zshrc 파일을 nano 편집기로 오픈한 화면입니다.
제일 하단에 다음과 같은 내용을 추가하였습니다.
# Java 11
# export JAVA_HOME=$(/usr/libexec/java_home -v 11)
# export PATH=${PATH}:$JAVA_HOME/bin:
# Java 1.8
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=${PATH}:$JAVA_HOME/bin:
저는 기존에 설정되어 있던 Java 11에서 Java 1.8로 바꾸기 위해 "Java 1.8"를 설정하였고, 그 외 부분은 주석 처리하였습니다.
독자분들은 본인 pc에 설치되어있는 Java 버전에 맞게 입력해 주시면 됩니다.
(보통 -v 옵션 뒤에 사용하고자 하는 Java의 메이저 버전을 입력하시면 됩니다.)
그 후, "source" 명령어를 통해, Mac 환경변수에 JAVA_HOME과 PATH를 변경하기 위해 .zshrc에 정의한 export 명령어가 동작할 수 있도록 하면 제 mac의 기본 Java 버전 변경은 완료가 됩니다.
$ source ~/.zshrc
2.2 변경된 Java version 확인하기
아래 명령어를 통해 기본 java version을 조회하면 제가 원하는 대로 기존 Java 11에서 Java 1.8로 버전이 변경된 것을 알 수 있습니다.
$ java -version
위의 일련에 과정을 진행하면서 저는 본격적으로 궁금한 점이 많이 생겼습니다.
아래에서 하나하나 천천히 풀어가도록 하겠습니다.
2-Q1. ~/.zshrc 에서 '~'(물결 표시)가 의미하는 것이 무엇인가?
mac(혹은 linux)에서 ~는 HOME 경로를 뜻합니다.
따라서 아래와 같은 명령어를 사용하면 HOME 경로로 정의된 곳으로 현재 위치를 이동하는 것을 알 수 있습니다.
# HOME 경로로 이동
$ cd ~
제 pc에 설정된 HOME 경로는 '/Users/yoonpunk'이며 현재 환경 변수로 설정되어 있는 HOME 경로를 확인하기 위해선 아래와 같이 전체 환경변수를 조회하거나 혹은 echo를 통해 HOME 경로를 출력해 확인할 수 있습니다.
# 전체 환경 변수 조회하기
$ env
# echo HOME 조회
$ echo ${HOME}
("echo ${HOME}"에서 "${}"의 의미는 는 바로 뒷 내용에서 확인하시기 바랍니다.)
2-Q2. .zshrc 파일은 무엇이며 왜 파일명에 '.'이 붙어 있는가?
.zshrc 파일은 쉽게 말해 zsh 쉘의 설정 파일입니다. 우리가 터미널을 켜서 사용하는 zsh의 설정 정보가 담긴 파일이라고 생각하시면 됩니다. 동작 방식은 우리가 mac을 켜고 로그인을 하게 되면 zsh이 항상 .zshrc파일을 읽고 그 안에 담긴 명령어를 실행하게 됩니다.
우리가 .zshrc에 정의한 여러 명령어가 zsh에 의해 실행되고, 그 명령어에 따른 여러 설정이 작동하게 되는 것입니다.
(위 내용은 아래에 나올 export 설명을 보시면 좀 더 이해하기 수월할 것입니다.)
그렇다면 이 파일명 제일 앞에 붙은 '.'의 의미는 무엇일까요?
파일명 제일 앞에 '.' 붙어 있는 파일은 Dot File로 Windows의 숨김 파일과 유사합니다.
파일명 제일 앞에 '.'을 붙여줌으로써 해당 파일을 숨김 처리할 수 있습니다.
이런 Dot File은 단순히 파일 조회 명령어인 'ls' 나 'll'을 통해 조회되지 않고, -a 옵션을 주어야 Dot File을 조회할 수 있습니다.
# Dot File 조회 불가
$ ll
# Dot File 조회 가능
$ ll -a
2-Q3. export는 무엇인가?
export는 쉘 변수를 현재 사용 중인 OS의 환경 변수로 저장하는 명령어입니다.
(OS 환경 변수는 OS 변수/시스템 변수/시스템 환경 변수/환경 변수처럼 다양하게 불립니다.)
쉘 변수는 우리가 터미널을 켰을 때, 터미널 내에서 사용하는 변수를 말하며, OS 환경 변수는 우리가 사용 중인 운영체제에서 사용하는 변수입니다.
아까 위에서 'env' 명령어를 통해 HOME경로를 살펴본 것이 OS 환경 변수입니다. HOME은 등록되어 있는 OS 환경 변수 중 하나입니다.
그럼, 쉘 변수와 OS 환경 변수 그리고, OS 환경 변수를 등록하는 방법을 조금 더 살펴보겠습니다.
쉘 변수
쉘 변수는 터미널 내에서 사용하는 변수입니다. 이러한 쉘 변수를 사용하기 위해선 터미널을 켜고 아래와 같이 쉘 변수를 할당하면 됩니다.
$ name='my name is yoonpunk'
$ echo $name
위에서 보시다시피 name에 'my name is yoonpunk'라는 문자열을 할당하고 echo 명령어를 통해 쉘 변수 name에 저장된 내용을 출력했습니다.
이 쉘 변수 name은 현재 name변수를 등록한 터미널 내에서만 사용되는 변수이기 때문에 지금 사용 중인 터미널을 종료하고 다시 시작해 echo 명령어를 통해 name을 출력한다면 아래와 같이 이미 해당 쉘 변수는 사라졌기 때문에 아무것도 출력되지 않게 됩니다.
다시 말해, 쉘 변수는 현재 사용중인 터미널 내에서만 저장하고 사용할 수 있는 변수입니다.
그렇다면, 한 번 등록한 쉘 변수를 제가 사용하는 터미널 혹은 여러 app에서 지속적으로 사용하려면 어떻게 해야 할까요?
바로 쉘 변수 대신 OS 환경 변수로 저장해 사용하면 됩니다.
OS 환경 변수
OS 환경 변수는 현재 사용 중인 운영체제 내 전역 변수로 OS 환경 아래 실행되는 모든 app에서 사용할 수 있는 변수입니다. OS 변수를 사용하기 위해서 바로 "export" 명령어를 사용합니다.
$ export name='my name is yoonpunk. It is global'
# 등록된 OS 환경 변수 조회
$ env
위 명령어를 통해 name을 OS 환경 변수에 등록했고, "env" 명령어를 통해 정상적으로 name 변수가 등록된 것을 확인할 수 있습니다. 하지만 이 방식 또한 사용자가 재부팅했을 경우 사라지게 됩니다.
그래서 항상 mac이 기동 될 때, OS 환경 변수로 자동 등록하기 위해 .zshrc 파일에 "export" 명령어를 정의하고 사용하게 됩니다.
위에서 Java 버전 설정을 위해 OS 변수 JAVA_HOME과 PATH를 설정하기 위한 "export"명령어를 .zshrc에 정의한 것이 바로 이 이유입니다.
항상 mac이 기동 될 때마다 .zshrc에 설정된 export 명령어를 실행시켜 JAVA_HOME과 PATH 변수를 원하는 Java 버전이 설치된 경로로 세팅해주는 것입니다.
자. 그럼 마지막 궁금증입니다. "export" 명령어를 통해 원하는 버전의 Java가 설치된 경로를 세팅해주면 되는데 왜 1번이 아닌 2번과 같이 설정을 했을까요?
# 1번
export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
export PATH=${PATH}:$JAVA_HOME/bin:
# 2번
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=${PATH}:$JAVA_HOME/bin:
2-Q4. $(/usr/libexec/java_home -v 1.8)과 ${PATH}:$JAVA_HOME/bin는 무엇인가?
위 질문에 답을 하기 전에 $()와 ${}에 대해 먼저 알아봅시다.
$() Command Substitution
$()란 command substitution으로 괄호 안에 명령어와 함께 사용합니다. 괄호 안의 명령어에 의해 실행된 결과를 $() 위치에 대체하여 사용할 수 있게 합니다.
# $(명령어) 사용 예제
$ CURRENT_DIR=$(pwd)
$ echo $CURRENT_DIR
위 예시처럼 pwd 명령어에 의해 실행된 결과인 '/Users/yoonpunk'가 '$(pwd)' 자리에 대체되어 쉘 변수 CURRENT_DIR 선언되고 echo로 출력한 결과를 확인할 수 있습니다.
${} Parameter Substitution
${}란 parameter substitution으로 괄호안에 변수(쉘/OS 변수)와 함께 사용 합니다. 괄호 안의 변수에 할당된 결과를 ${} 위치에 대체하여 사용할 수 있게 합니다.
# ${변수명} 사용 예제
# 1번
$ echo $CURRENT_DIR
# 2번
$ echo ${CURRENT_DIR}
# 3번 (CURRENT_DIR_is 까지 변수명으로 인식)
$ echo $CURRENT_DIR_is new directory.
# 4번 (CURRENT_DIR 까지 변수명으로 인식)
$ echo ${CURRENT_DIR}_is new directory.
위 예시를 보면 알 수 있듯이 1번과 2번 예시처럼 '{}'괄호를 생략해도 변수에 할당된 결과를 동일하게 사용할 수 있습니다.
하지만 3번과 4번 예시처럼 'CURRENT_DIR' 변수 뒤에 추가 정보를 이어 붙여 사용할 경우, 별도의 '{}' 괄호를 통해 변수명을 정확히 규정하지 않으면 $뒤로 띄어쓰기가 나타날 때 까지를 전체 변수명으로 인식하기 때문에 CURRENT_DIR 변수를 올바르게 사용하지 못하게 됩니다.
따라서, 4번 예시처럼 여러 변수 혹은 값을 이어서 사용할 경우, 변수명을 정확하게 구분 짓기 위해 '{}'괄호를 사용하면 됩니다.
그럼 다시 원래의 질문으로 돌아가 보겠습니다.
이제 $()와 ${}을 배웠으니 아래의 명령어가 무슨 의미인지 쉽게 파악되실 겁니다.
# 2번
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=${PATH}:$JAVA_HOME/bin:
'$(/usr/libexec/java_home -v 1.8' 명령어의 결과인 Java 1.8 설치경로를 OS 환경 변수 JAVA_HOME에 할당하고, 기존에 사용 중인 OS 환경 변수인 PATH에 위에서 방금 등록한 JAVA_HOME에 ':'과 '/bin'을 결합해 재할당한 것으로 이해할 수 있습니다.
($뒤에 '{}'가 없음에도 '/bin'과 올바르게 결합되어 사용된 것으로 보아 '/' 사용 시에는 띄어쓰기 공백과 같이 변수명을 구분해주는 것 같습니다.)
위 결과에서 보면 알 수 있듯이 우리가 의도한 대로 JAVA_HOME에 Java 1.8 버전 설치 경로가 할당되었으며, 기존에 설정된 PATH 뒤에 추가로 JAVA_HOME 경로가 ':'과 '/bin'이 결합되어 올바르게 설정되었음을 알 수 있습니다.
지금까지 우리는 Mac 환경에서의 Java 버전 관리 방법을 알아보았습니다.
Mac 혹은 Linux에 처음 Java 개발 환경을 구축하시려는 분들에게 이 글이 조금이나마 도움이 되었으면 좋겠습니다.
질문 혹은 오류 정정 댓글은 언제나 환영입니다.
감사합니다.