Adobe Experience Manager(AEM) 패키지는 콘텐츠 관리의 숨겨진 영웅입니다. 코드와 구성에서 중요한 콘텐츠에 이르기까지 모든 것을 묶는 강력한 컨테이너입니다. 하지만 솔직히 말해서, 이러한 패키지를 수동으로 만들고, 구성하고, 다운로드하는 것은 지루한 클릭 춤처럼 느껴질 수 있습니다.
몇 번의 키보드 입력으로 이 과정을 자동화해 일관성, 속도, 안정성을 보장하고 작업 부담을 줄일 수 있다면 어떨까요?
AEM 개발자와 관리자가 패키지 관리자 API를 사용하는 방법에 대한 스크립트를 뒤집는 Bash 스크립트를 보여드리겠습니다(말장난 의도!) 몇 초 만에 패키지를 만들고, 즉석에서 필터를 조정하고, 수술적 정밀함으로 백업을 잡아내는 것을 생각해보세요. 커피가 완벽한 음용 온도로 식기 전에 말이죠. ☕
들어가기 전에 잠깐 알려드릴 말씀이 있습니다 . 이 글은 심층적으로 다루고, 세심하게 자세하며, 거침없이 기술적인 내용을 다룹니다. 스크립트의 논리를 분석하고, AEM API의 복잡한 사항을 살펴보고, 엣지 케이스를 해결해 보겠습니다. 바로 코드로 넘어가고 싶은 개발자라면 이 글의 맨 아래로 이동하면 됩니다. 하지만 자동화의 원리와 이유를 이해하기 위해 여기 왔다면, 안전띠를 매세요. 토끼굴까지 내려가겠습니다. 🕳️
create-remote-aem-pkg.sh
스크립트는 AEM의 패키지 관리자 API와의 상호작용을 자동화하여 패키지 생성, 구성 및 배포에 대한 체계적인 접근 방식을 제공합니다. 개발자와 관리자를 위해 설계되었으며, 일관성과 안정성을 강조하는 명령줄 기반 프로세스로 수동 워크플로를 대체합니다.
/content/dam
, /apps
)를 프로그래밍 방식으로 정의합니다.curl
통해 기본¹ 자격 증명 기반 인증을 지원합니다./etc
또는 /apps
의 안정적인 상태를 캡처합니다. ./create-remote-aem-pkg.sh admin securepass123 localhost 4502 backup-group "Content Backup" /backups /content/dam /etc/clientlibs
이 명령은 /content/dam
및 /etc/clientlibs
포함하여 backup-group
그룹 아래에 "Content Backup"이라는 이름의 패키지를 생성하고 출력을 /backups
디렉토리에 저장합니다.
create-remote-aem-pkg.sh
스크립트를 분석해 보겠습니다(기사 하단에서 찾을 수 있습니다). 이 스크립트가 AEM 패키지 관리를 어떻게 조율하는지 이해해 보겠습니다. 이 스크립트의 구조, 주요 기능, 워크플로 로직에 집중하겠습니다. 도구를 사용자 지정하거나 디버깅하려는 개발자에게 이상적입니다.
_log()
: 명확한 감사 추적을 위해 메시지 앞에 타임스탬프를 붙이는 유틸리티 함수입니다. _log () { echo "[$(date +%Y.%m.%d-%H:%M:%S)] $1" }
이것이 중요한 이유 : 모든 작업(예: "패키지 빌드")이 컨텍스트와 함께 기록되도록 하여 문제 해결을 간소화합니다.
check_last_exec()
: 종료 코드와 API 응답을 확인하여 이전 명령의 성공 여부를 검증합니다. check_last_exec () { # Checks $? (exit status) and $CURL_OUTPUT for errors if [ "$status" -ne 0 ] || [[ $output =~ .*success\":false* ]]; then _log "Error detected!"; exit 1; fi }
중요성 : 인증 문제나 잘못된 경로와 같은 심각한 오류가 발생할 경우 실행을 중단하여 조용한 실패를 방지합니다.
스크립트는 동적 필터가 뒤따르는 7개의 위치 인수를 허용합니다.
USR="$1" # AEM username PWD="$2" # AEM password SVR="$3" # Server host (eg, localhost) PORT="$4" # Port (eg, 4502) PKG_GROUP="$5" # Package group (eg, "backups") PKG_NAME="$6" # Package name (eg, "dam-backup") BK_FOLDER="$7" # Backup directory (eg, "/backups") shift 7 # Remaining arguments become filters (eg, "/content/dam")
위치 인수는 단순성을 보장하는 반면, shift
가변 필터 경로를 유연하게 처리합니다.
PKG_NAME
의 공백을 밑줄로 바꿉니다. PKG_NAME=${PKG_NAME// /_}
curl
사용하여 패키지를 나열하여 중복 생성을 방지합니다. if [ $(curl ... | grep "$PKG_NAME.zip" | wc -l) -eq 1 ]; then _log "Package exists—skipping creation." else curl -X POST ... # Creates the package fi
입력 경로에서 필터의 JSON 배열을 구성합니다.
FILTERS_PARAM="" for i in "${!FILTERS[@]}"; do FILTERS_PARAM+="{\"root\": \"${FILTERS[$i]}\", \"rules\": []}" # Adds commas between entries, but not after the last done
출력 예 :
[{"root": "/content/dam"}, {"root": "/apps"}]
이 JSON은 AEM의 /crx/packmgr/update.jsp
엔드포인트를 통해 패키지 정의에 삽입됩니다.
build
명령을 사용하여 컴파일을 트리거합니다. curl -X POST … -F "cmd=build"
참고 : 스크립트는 빌드가 완료될 때까지 기다렸다가 진행합니다.
curl
사용하여 .zip
파일을 가져오고 타임스탬프가 포함된 파일 이름으로 저장합니다. BK_FILE="$PKG_NAME-$(date +%Y%m%d-%H%M%S).zip" curl -o "$BK_FOLDER/$BK_FILE" ...
create-remote-aem-pkg.sh
와 같은 무인 스크립트의 경우 강력한 오류 처리 및 로깅이 중요하며, 이를 통해 실패를 일찍 포착하고 명확하게 로깅할 수 있습니다. 스크립트가 예상치 못한 문제로부터 보호하고 실행 가능한 통찰력을 제공하는 방법은 다음과 같습니다.
_log
함수는 모든 메시지에 [YYYY.MM.DD-HH:MM:SS]
타임스탬프를 접두사로 지정하여 디버깅을 위한 감사 추적을 생성합니다. _log "Starting backup process..." # Output: [2023.10.25-14:30:45] Starting backup process...
중요성 : 타임스탬프는 스크립트 활동과 AEM 서버 로그 또는 외부 이벤트(예: cron 작업 일정)를 연관시키는 데 도움이 됩니다.
비행 전 점검 :
BK_FOLDER
)의 존재 여부를 확인합니다. if [ ! -d "$BK_FOLDER" ]; then _log "Backup folder '$BK_FOLDER' does not exist!" && exit 1 fi
PKG_NAME
정리합니다.
API 응답 검증 :
check_last_exec
함수는 셸 종료 코드( $?
)와 AEM API 응답을 모두 검사합니다.
check_last_exec "Error message" "$CURL_OUTPUT" $CURL_STATUS
curl
네트워크 오류)은 즉시 종료됩니다.
success\":false
JSON 응답 또는 "HTTP ERROR" 문자열을 감지합니다.
3.3 HTTP 상태 확인 : 패키지를 다운로드할 때 스크립트는 200
상태 코드를 확인합니다.
if [ "$(curl -w "%{http_code}" ...)" -eq "200" ]; then # Proceed if download succeeds else _log "Error downloading the package!" && exit 1 fi
check_last_exec
401 Unauthorized
응답을 포착하고 명확한 오류 메시지와 함께 종료됩니다.success:false
반환하고, 스크립트는 "필터 추가 오류"를 기록하고 종료됩니다.BK_FILE
쓰기에 실패했습니다. -s
플래그로 파일 크기를 확인하고 종료하기 전에 경고합니다.curl
0이 아닌 코드와 함께 종료되고, 스크립트는 "패키지 빌드 오류"를 기록합니다.curl -k
사용하는데, 이는 SSL 검증을 건너뜁니다. 프로덕션을 위한 권장 사항 : CA 번들을 지정하려면 --cacert
로 대체합니다.
$AEM_PASSWORD
)를 사용합니다.set -x
추가하여 실행된 명령을 출력합니다.curl
명령을 실행하여 문제를 분리합니다. ./create-remote-aem-pkg.sh ... >> /var/log/aem_backup.log 2>&1
create-remote-aem-pkg.sh
스크립트는 시작점으로 설계되었으며, 팀의 요구 사항에 맞게 수정할 수 있는 기반입니다. 아래는 기능을 확장하거나 특정 사용 사례에 맞게 조정하기 위한 일반적인 사용자 정의와 구현 지침입니다.
기본 파일 이름은 타임스탬프( $PKG_NAME-$(date +%Y%m%d-%H%M%S).zip
)를 사용합니다. 환경 이름, 프로젝트 ID 또는 의미적 버전을 포함하도록 이를 수정합니다.
# Example: Include environment (eg, "dev", "prod") BK_FILE="${PKG_NAME}-${ENV}-$(date +%Y%m%d).zip" # Example: Add Git commit SHA for traceability COMMIT_SHA=$(git rev-parse --short HEAD) BK_FILE="${PKG_NAME}-${COMMIT_SHA}.zip"
팁 : 날짜/시간 형식에서는 파일 이름에 사용할 수 없는 문자(예: Windows의 콜론 :
)를 사용하지 않도록 주의하세요.
스크립트는 동적 경로를 필터로 허용하지만 자주 사용되는 경로를 하드코딩하거나 제외 항목을 추가할 수도 있습니다.
# Hardcode essential paths (eg, "/var/audit") DEFAULT_FILTERS=("/content/dam" "/apps" "/var/audit") FILTERS=("${DEFAULT_FILTERS[@]}" "${@}") # Merge with command-line inputs # Add exclusion rules (requires AEM API support) FILTERS_PARAM+="{\"root\": \"${FILTERS[$i]}\", \"rules\": [{\"modifier\": \"exclude\", \"pattern\": \".*/test/*\"}]}"
일반 텍스트 비밀번호 사용 금지 :
환경 변수나 비밀 관리자를 사용하여 자격 증명을 삽입합니다.
# Fetch password from environment variable PWD="$AEM_PASSWORD" # Use AWS Secrets Manager (example) PWD=$(aws secretsmanager get-secret-value --secret-id aem/prod/password --query SecretString --output text)
SSL 검증 시행 :
curl -k
(안전하지 않음)를 신뢰할 수 있는 CA 인증서로 바꾸세요.
curl --cacert /path/to/ca-bundle.crt -u "$USR":"$PWD" ...
다운로드가 성공적으로 완료된 후 다운스트림 프로세스를 트리거하도록 스크립트를 확장합니다.
# Example: Upload to cloud storage aws s3 cp "$BK_FOLDER/$BK_FILE" s3://my-backup-bucket/ # Example: Validate package integrity CHECKSUM=$(sha256sum "$BK_FOLDER/$BK_FILE" | cut -d ' ' -f 1) _log "SHA-256 checksum: $CHECKSUM" # Example: Clean up old backups (retain last 7 days) find "$BK_FOLDER" -name "*.zip" -mtime +7 -exec rm {} \;
Slack, 이메일 또는 모니터링 도구를 통해 팀에 성공/실패 알림:
# Post to Slack on failure curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"🚨 AEM backup failed: $(hostname)\"}" \ https://hooks.slack.com/services/YOUR/WEBHOOK/URL # Send email via sendmail if [ $? -ne 0 ]; then echo "Subject: Backup Failed" | sendmail [email protected] fi
AEM 패키지를 관리하는 것은 수동적이고 오류가 발생하기 쉬운 일이 될 필요가 없습니다. create-remote-aem-pkg.sh
스크립트를 사용하면 패키지 생성, 필터링 및 배포를 간소화되고 반복 가능한 프로세스로 변환할 수 있습니다. 이 도구는 단순히 시간을 절약하는 것이 아니라 AEM 운영에서 일관성, 안정성 및 확장성을 가능하게 하는 것입니다.
자동화의 이점 : 반복적인 GUI 상호작용을 없앰으로써 스크립트는 인적 오류를 줄이고 팀은 보다 가치 있는 작업에 집중할 수 있게 되었습니다.
유연성이 중요합니다 . 중요한 콘텐츠의 백업, 환경 동기화, 업데이트 준비 등에 관계없이 스크립트는 최소한의 조정으로 다양한 사용 사례에 적응합니다.
복원성이 핵심입니다 . 내장된 로깅, 오류 검사, 보안 고려사항을 통해 상황이 잘못되더라도 스크립트가 예측 가능하게 동작하도록 보장합니다.
훌륭한 도구는 실제 세계의 과제에서 탄생합니다. 이 스크립트는 시작점입니다. 팀의 요구 사항이 커짐에 따라 이를 기반으로 구축할 수 있다고 생각하세요. 솔로 개발자이든 대규모 DevOps 팀의 일원이든, 이와 같은 자동화는 코드에 대한 작은 투자가 생산성과 마음의 평화에 있어 엄청난 수익을 낼 수 있는 방법을 보여줍니다.
다음 단계로 나아갈 준비가 되셨나요?
따라와 주셔서 감사합니다. 이제 자동화를 시작해보세요! 🚀
#!/bin/bash set -eo pipefail # The script will create a package thought the package manager api: # - The package is created, if not already present # - Package filters are populated accordingly to specified paths # - Package is builded # - Package is download to the specified folder _log () { echo "[$(date +%Y.%m.%d-%H:%M:%S)] $1" } check_last_exec () { local message="$1" local output="$2" local status=$3 if [ "$status" -ne 0 ]; then echo && echo "$message" && echo exit 1 fi if [[ $output =~ .*success\":false* ]] || [[ $output =~ .*"HTTP ERROR"* ]]; then _log "$message" exit 1 fi } USR="$1" PWD="$2" SVR="$3" PORT="$4" PKG_GROUP="$5" PKG_NAME="$6" BK_FOLDER="$7" shift 7 # The following paths will be included in the package FILTERS=($@) BK_FILE=$PKG_NAME"-"$(date +%Y%m%d-%H%M%S).zip _log "Starting backup process..." echo "AEM instance: '$SVR':'$PORT' AEM User: '$USR' Package group: $PKG_GROUP Package name: '$PKG_NAME' Destination folder: $BK_FOLDER Destination file: '$BK_FILE' Filter paths: " printf '\t%s\n\n' "${FILTERS[@]}" if [ ! -d "$BK_FOLDER" ]; then _log "Backup folder '$BK_FOLDER' does not exist!" && echo exit 1 fi PKG_NAME=${PKG_NAME// /_} check_last_exec "Error replacing white space chars from package name!" "" $? || exit 1 _log "Removed whitespaces from package name: '$PKG_NAME'" BK_FILE=$PKG_NAME.zip _log "Backup file: '$BK_FILE'" _log "Creating the package..." if [ $(curl -k -u "$USR":"$PWD" "$SVR:$PORT/crx/packmgr/service.jsp?cmd=ls" 2>/dev/null | grep "$PKG_NAME.zip" | wc -l) -eq 1 ]; then _log " Package '$PKG_GROUP/$PKG_NAME' is already present: skipping creation." else curl -k --silent -u "$USR":"$PWD" -X POST \ "$SVR:$PORT/crx/packmgr/service/.json/etc/packages/$PKG_GROUP/$PKG_NAME?cmd=create" \ -d packageName="$PKG_NAME" -d groupName="$PKG_GROUP" check_last_exec " Error creating the package!" "" $? _log " Package created" fi # create filters variable FILTERS_PARAM="" ARR_LEN="${#FILTERS[@]}" for i in "${!FILTERS[@]}"; do FILTERS_PARAM=$FILTERS_PARAM"{\"root\": \"${FILTERS[$i]}\", \"rules\": []}" T=$((i+1)) if [ $T -ne $ARR_LEN ]; then FILTERS_PARAM=$FILTERS_PARAM", " fi done # add filters _log "Adding filters to the package..." CURL_OUTPUT=$(curl -k --silent -u "$USR":"$PWD" -X POST "$SVR:$PORT/crx/packmgr/update.jsp" \ -F path=/etc/packages/"$PKG_GROUP"/"$PKG_NAME".zip -F packageName="$PKG_NAME" \ -F groupName="$PKG_GROUP" \ -F filter="[$FILTERS_PARAM]" \ -F "_charset_=UTF-8") CURL_STATUS=$? # Pass the status to the check_last_exec function check_last_exec "Error adding filters to the package!" "$CURL_OUTPUT" $CURL_STATUS _log " Package filters updated successfully." # build package _log "Building the package..." CURL_OUTPUT=$(curl -k -u "$USR":"$PWD" -X POST \ "$SVR:$PORT/crx/packmgr/service/script.html/etc/packages/$PKG_GROUP/$PKG_NAME.zip" \ -F "cmd=build") check_last_exec " Error building the package!" "$CURL_OUTPUT" $? _log " Package built." # download package _log "Downloading the package..." if [ "$(curl -w "%{http_code}" -o "$BK_FOLDER/$BK_FILE" -k --silent -u "$USR":"$PWD" "$SVR:$PORT/etc/packages/$PKG_GROUP/$PKG_NAME.zip")" -eq "200" ]; then if [ -f "$BK_FOLDER/$BK_FILE" ] && [ -s "$BK_FOLDER/$BK_FILE" ]; then _log " Package $BK_FILE downloaded in $BK_FOLDER." exit 0 fi fi _log " Error downloading the package!" exit 1
[¹] curl -k
사용하여 SSL 검증을 건너뛰는 것은 테스트용으로는 편리하지만 프로덕션에서는 좀 더 강력한 것이 필요할 것입니다(예: --cacert
)!