클라우드 이력서 챌린지: CI/CD, Python, 그리고 보안 위협을 극복하기
Source: Dev.to
소개
이론적 지식은 기본이지만, 엔지니어링 역량은 구현을 통해 쌓입니다.
S3, Lambda, DynamoDB와 같은 서비스의 이론을 이해하는 것만으로는 충분하지 않다는 것을 깨달았습니다—보안되고 자동화된, 프로덕션 수준의 애플리케이션으로 이들을 조정할 수 있음을 보여주고 싶었습니다.
Cloud Resume Challenge(Forrest Brazeal이 만든)는 아키텍처 개념과 실제 DevOps 구현 사이의 격차를 메우기에 완벽한 프레임워크였습니다.
서버리스 이력서 웹사이트를 구축하고, GitHub Actions로 배포를 자동화했으며, 그 과정에서 엔지니어링 장애물을 해결한 방법을 소개합니다.
아키텍처
프로젝트 요구사항은 간단합니다: “온라인 이력서를 호스팅한다.”
제가 구현한 아키텍처는 순수 클라우드 엔지니어링입니다:
| 계층 | 기술 |
|---|---|
| 프론트엔드 | HTML/CSS를 S3에 호스팅하고, 전 세계 캐싱을 위해 CloudFront(HTTPS)로 가속화 |
| 백엔드 | AWS Lambda(Python)으로 API 요청을 처리 |
| 데이터베이스 | 방문자 수를 저장하기 위한 DynamoDB(NoSQL) |
| 인프라스트럭처 | GitHub Actions을 사용한 완전 자동화 CI/CD 파이프라인 |
Level 1: 프론트엔드 접착제 (JavaScript)
웹사이트 자체는 정적 HTML/CSS이지만, 동적으로 만들기 위해서는 JavaScript가 필요했습니다. 저는 API Gateway 트리거에서 방문자 수를 가져오는 스크립트를 작성했습니다.
가장 큰 장애물은 CORS(Cross‑Origin Resource Sharing)였습니다. aminetraibi.com에서 실행되는 제 JavaScript가 AWS Lambda URL과 통신하려고 했지만, 브라우저가 보안상의 이유로 차단했습니다.
해결책: Python Lambda 함수가 Access-Control-Allow-Origin: * 헤더를 반환하도록 설정하여 브라우저가 응답을 받아들이도록 합니다.
// Fetching the view count
fetch(apiUrl)
.then(response => response.json())
.then(data => {
document.getElementById('counter').innerText = data.views;
});레벨 2: 백엔드 로직 (Python & DynamoDB)
페이지가 로드될 때마다 증가하는 원자 카운터가 필요했습니다. 저는 boto3를 사용하여 DynamoDB와 통신하는 Python 스크립트를 작성했습니다.
특정한 어려움은 DynamoDB 예약어를 처리하는 것이었습니다. views가 예약어이기 때문에, 저는 ExpressionAttributeNames를 사용하여 올바르게 매핑했습니다.
# Atomic update using UpdateExpression
response = table.update_item(
Key={'id': 'page_view'},
UpdateExpression='SET #v = #v + :val',
ExpressionAttributeNames={'#v': 'views'},
ExpressionAttributeValues={':val': 1},
ReturnValues='UPDATED_NEW'
)Level 3: 보안 교훈 🚨
이것은 내 학습 여정에서 가장 중요한 부분이었습니다.
시크릿 관리에 대해 힘든 교훈을 얻었습니다. 빌드 초기에 실수로 자격 증명을 저장소에 커밋했습니다.

즉시 위험을 파악하고 IAM에서 키를 폐기했으며 GitHub Secrets로 이전했습니다.
핵심 정리
- 최소 권한을 가진 전용 IAM 사용자를 사용하세요.
- CI/CD 파이프라인을 통해 런타임에만 키를 주입하세요.
- 절대 시크릿을 소스 코드에 저장하지 마세요.
레벨 4: 자동 테스트 및 목킹
이 챌린지는 Python 단위 테스트가 필요했으며, 이는 가장 어려운 부분 중 하나였습니다.
로컬에서 테스트를 실행했을 때 boto3가 실제 DynamoDB에 연결하려고 했습니다. CI/CD 환경에서는 테스트가 인터넷 연결이나 실시간 데이터베이스 권한에 의존하지 않기를 원했습니다.
해결책: 목킹
unittest.mock 라이브러리를 사용해 AWS 서비스를 시뮬레이션했습니다. DynamoDB 테이블 리소스를 목킹함으로써 실제 API 호출 없이 함수 로직을 테스트할 수 있었습니다.

# Using MagicMock to fake the database response
mock_table = MagicMock()
mock_table.update_item.return_value = {'Attributes': {'views': 123}}
lambda_function.table = mock_table이렇게 하면 파이프라인이 빠르고 견고하며 테스트 중에 AWS 비용이 발생하지 않습니다.
레벨 5: CI/CD (그린 체크마크)
궁극적인 목표는 AWS 콘솔(“ClickOps”)에서 벗어나 올바른 DevOps 관행을 도입하는 것이었습니다.
두 개의 별도 GitHub Actions 워크플로를 만들었습니다:
| 워크플로 | 트리거 | 작업 |
|---|---|---|
| 프론트엔드 파이프라인 | HTML/CSS 변경 | S3에 동기화, CloudFront 캐시 무효화 |
| 백엔드 파이프라인 | Python 코드 변경 | 단위 테스트 실행, 코드 압축, Lambda 함수 업데이트 |
이제 git push를 main에 간단히 하면 전체 애플리케이션이 자동으로 업데이트됩니다.

결론 및 다음 단계
이 프로젝트를 통해 스택의 모든 부분을 다루게 되었습니다: 네트워킹(DNS/Route53), 보안(IAM), 컴퓨트(Lambda), 데이터베이스(DynamoDB), 그리고 프론트엔드 로직(JavaScript).
다음 마일스톤은 이 기반 위에서 계속 구축하는 것으로, 구체적으로는 Terraform을 사용한 인프라스트럭처 코드(IaC)를 깊이 파고들고 Docker/ECS를 활용한 컨테이너화 탐색입니다.
실제로 해보면서 배우고, 문제를 일으키고, 해결하는 클라우드 엔지니어를 찾고 계시다면—연락 주세요!
Live Site: https://aminetraibi.com
GitHub Repository: https://github.com/AmineTra/cloud-resume-challenge

