셸과 환경 설정
70분 안팎
학습 목표
경로·인용·권한·출력·오류·종료 상태를 구분합니다.
개념
설정 오류를 업무 오류보다 먼저 발견합니다
동아리 API가 개발 PC에서는 실행되지만 다른 실행 폴더에서는 빈 목록을 보여 준다고 가정합니다. 코드가 달라진 것이 아니라 상대 경로가 다른 H2 파일을 만든 것일 수 있습니다. 배포 담당자는 HTTP 오류를 고치기 전에 어떤 작업 디렉터리에서 어떤 저장소로 연결했는지 확인합니다. 이 레슨에서는 셸을 단순 명령 모음이 아니라 프로그램 실행의 경계를 만드는 도구로 사용합니다. 설정이 없으면 우연한 기본값으로 실행하지 않고 이름만 남긴 뒤 종료하도록 구현합니다.
실습 환경과 파일을 준비합니다
이 모듈의 로컬 과제는 압축을 푼 starter 폴더에서 실행합니다. JDK 17, Python 3, Bash와 Maven Wrapper가 필요합니다. Java 의존성은 지도자가 준비한 캐시를 사용하며 학습자는 ./mvnw test를 입력합니다. 작성 검증은 오프라인 옵션으로 진행했습니다. Linux 컨테이너 과제는 Docker가 준비된 별도 환경에서 검사하며 external 표시는 아직 실행 확인이 남았다는 의미입니다. 운영 호스트나 실제 계정은 사용하지 않습니다. 각 과제의 solution은 비교용이며 테스트를 지워 통과시키지 않습니다.
경로는 문자열 한 개로 전달합니다
상대 경로는 현재 폴더를 기준으로 해석됩니다. 같은 run.sh라도 호출자가 위치를 바꾸면 ./config가 가리키는 곳이 달라집니다. 실행용 설정에는 절대 경로를 사용하고 검사 스크립트는 자기 파일이 있는 폴더로 이동합니다. APP_CONFIG가 /tmp/club config/app.properties라면 "$APP_CONFIG"처럼 큰따옴표로 감싸야 합니다. 따옴표가 없으면 공백에서 인자가 나뉘어 파일이 있어도 찾지 못합니다. 공백을 없애는 방식으로 피하지 않고 경계 사례를 테스트에 남깁니다.
셸 변수와 자식 환경을 구분합니다
CATALOG_JDBC_URL=value는 현재 셸의 변수를 만듭니다. Java 프로세스가 읽게 하려면 export하거나 명령 앞에 변수 할당을 둡니다. 예를 들어 CATALOG_JDBC_URL=jdbc:h2:file:/data/club sh run.sh는 그 호출에 설정을 전달합니다. run.sh는 APP_CONFIG에서 받은 파일 위치를 SPRING_CONFIG_ADDITIONAL_LOCATION으로 export합니다. Spring이 읽는 catalog.jdbc.url과 스크립트가 사용하는 APP_CONFIG는 담당자가 다르므로 이름이 비슷하다는 이유로 서로 바꿔 쓰지 않습니다.
설정 파일을 실행 코드로 읽지 않습니다
이번 설정 파일은 Spring properties입니다. server.port=8080 같은 키는 셸 할당 문법이 아닙니다. source로 읽으면 command not found가 발생할 수 있고 파일에 들어 있는 셸 명령이 실행되는 위험도 생깁니다. 스크립트는 파일의 위치와 읽기 가능 여부만 검사하고 내용 해석은 Spring에 맡깁니다. 값은 eval로 재해석하지 않습니다. 비밀값이 없는 application.properties.example과 실행자가 별도로 보관하는 실제 파일을 구분하면 산출물을 다시 빌드하지 않고 실행 환경을 바꿀 수 있습니다.
표준 출력과 오류 출력의 독자를 나눕니다
정상 데이터나 기계가 읽을 결과는 표준 출력으로, 실패 원인은 표준 오류로 보냅니다. echo 명령 뒤의 >&2는 오류 채널로 보낸다는 뜻입니다. CONFIG_MISSING: CATALOG_JDBC_URL은 변수 이름만 알리고 URL 값은 노출하지 않습니다. 셸에서 환경 전체를 출력하거나 set -x를 켜면 URL 또는 다른 인증정보가 로그에 섞일 수 있습니다. 진단 문장을 길게 만드는 것보다 어떤 필수 설정이 빠졌는지 안전하게 알려 주는 것이 이 경계의 역할입니다.
종료 상태를 출력 문장과 함께 읽습니다
프로그램의 종료 상태 0은 성공, 0이 아닌 값은 실패를 나타냅니다. 이 과제는 잘못된 실행 설정을 2로 정했습니다. 테스트는 메시지뿐 아니라 상태와 출력 채널까지 검사합니다. 오류를 출력하고 마지막 echo가 성공하면 전체 상태가 0이 되어 호출자가 배포를 계속할 수 있습니다. 따라서 누락 검사에서 exit 2로 끝내야 합니다. 다른 명령을 입력하기 전에 상태를 저장합니다. $?는 가장 최근에 끝난 명령의 상태라서 echo나 파일 조회 후에는 원래 실패값을 잃습니다.
권한은 실행자 입장에서 확인합니다
읽을 수 있는 파일인지 -r로 검사하고 셸 파일의 직접 실행에는 실행 권한도 필요합니다. sh run.sh는 sh가 내용을 읽어 실행하므로 run.sh 자체의 실행 비트와 구별됩니다. 실제 설정은 신뢰한 계정만 읽게 chmod 600으로 제한합니다. 컨테이너의 다른 UID가 읽는 파일은 그 계정에 맞춰 소유권 또는 최소 읽기 권한을 설계해야 합니다. Permission denied를 만났다고 모든 파일을 777로 바꾸면 설정을 다른 사용자가 수정할 수 있어 실행 경계를 더 약하게 만듭니다.
exec가 맡는 일을 이해합니다
스크립트의 마지막 exec java는 셸 프로세스를 Java로 교체합니다. 자식 Java를 띄우고 셸이 계속 기다리는 구조와 다릅니다. 이 레슨에서는 JAVA_BIN을 가짜 실행기로 바꾸어 -jar와 APP_JAR이 각각 하나의 인자로 전달되는지 검사합니다. 가짜 실행기는 서버도 DB도 만들지 않습니다. 따라서 이 검사의 통과는 셸 인용과 환경 전달의 근거이며 Spring 기동 성공의 근거는 아닙니다. 다음 레슨에서 산출물 자체를, 컨테이너 레슨에서 실제 HTTP를 따로 확인합니다.
set 옵션의 범위를 과장하지 않습니다
set -u는 정의되지 않은 변수 참조를 오류로 만들지만 ${NAME:-}는 미정의 값을 빈 문자열로 취급하여 직접 메시지를 만들 수 있습니다. set -e는 많은 실패를 조기에 멈추게 하지만 조건식과 파이프라인 등 문맥에 따라 동작이 달라집니다. 이를 모든 오류를 자동 처리하는 기능으로 설명하지 않습니다. check.sh는 의도적으로 실패할 명령 주변에서 set +e로 상태를 수집한 뒤 set -e를 복원합니다. 실행 스크립트에는 필수 입력에 대한 명시적인 if와 exit를 남깁니다.
오류를 재현 가능한 기록으로 남깁니다
starter의 TODO는 누락된 DB 설정에서 오류 메시지만 찍고 다음 단계로 진행합니다. check.sh의 FAIL: missing DB setting을 보면 설정을 추가하여 숨기지 말고 누락 경로의 종료를 고칩니다. 수정 뒤 정상 URL, 빈 URL, 공백 파일 경로, 존재하지 않는 파일, 상대 파일 위치를 확인합니다. 제출 기록에는 입력의 종류와 종료 상태를 적고 실제 연결값은 적지 않습니다. env의 다른 옵션과 환경 상속 문제는 더 읽기에서 이어가며 여기서는 안전한 실행 경계를 직접 완성합니다.
따라하기
실패 경계를 실행합니다
starter 폴더에서 실행하고 FAIL: missing DB setting을 읽습니다. run.sh의 DB 누락 분기가 어디까지 진행하는지 확인합니다.
bash check.sh누락 경로를 종료합니다
run.sh의 TODO를 exit 2로 바꿉니다. 오류 메시지는 변수 이름만 남기고 정상 출력을 만들지 않도록 합니다.
수정 후 다섯 사례를 확인합니다
수정한 starter에서 같은 검사를 다시 실행합니다. 아래는 solution에서 실제 수집한 한 줄 결과입니다.
bash check.sh실행 결과
PASS: shell configuration 5 cases
각 채널의 의미를 설명합니다
check.sh에서 누락 입력의 상태 2, 비어 있는 stdout, stderr 메시지 검사를 찾습니다. 가짜 Java가 인자 경계와 설정 전달만 확인한다는 범위를 제출 설명에 적습니다.
확인 문제
실습
run.sh의 DB 설정 누락 TODO를 exit 2로 고칩니다. check.sh를 수정하지 않고 누락·빈 값·공백 경로·읽기 실패·상대 경로를 확인합니다. 실제 설정값을 노출하지 않은 결과 기록을 제출합니다.
실행 명령
bash check.sh
기대 결과
PASS: shell configuration 5 cases
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- 실행 환경에서 API 오류를 추적하는 순서를 설명합니다.