클라우드 이력서 챌린지: CI/CD, Python, 그리고 보안 위협을 극복하기

발행: (2026년 3월 14일 오전 08:11 GMT+9)
8 분 소요
원문: Dev.to

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: 보안 교훈 🚨

이것은 내 학습 여정에서 가장 중요한 부분이었습니다.

시크릿 관리에 대해 힘든 교훈을 얻었습니다. 빌드 초기에 실수로 자격 증명을 저장소에 커밋했습니다.

Sweating

즉시 위험을 파악하고 IAM에서 키를 폐기했으며 GitHub Secrets로 이전했습니다.

핵심 정리

  • 최소 권한을 가진 전용 IAM 사용자를 사용하세요.
  • CI/CD 파이프라인을 통해 런타임에만 키를 주입하세요.
  • 절대 시크릿을 소스 코드에 저장하지 마세요.

레벨 4: 자동 테스트 및 목킹

이 챌린지는 Python 단위 테스트가 필요했으며, 이는 가장 어려운 부분 중 하나였습니다.

로컬에서 테스트를 실행했을 때 boto3가 실제 DynamoDB에 연결하려고 했습니다. CI/CD 환경에서는 테스트가 인터넷 연결이나 실시간 데이터베이스 권한에 의존하지 않기를 원했습니다.

해결책: 목킹

unittest.mock 라이브러리를 사용해 AWS 서비스를 시뮬레이션했습니다. DynamoDB 테이블 리소스를 목킹함으로써 실제 API 호출 없이 함수 로직을 테스트할 수 있었습니다.

Hackerman

# 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 pushmain에 간단히 하면 전체 애플리케이션이 자동으로 업데이트됩니다.

Success

결론 및 다음 단계

이 프로젝트를 통해 스택의 모든 부분을 다루게 되었습니다: 네트워킹(DNS/Route53), 보안(IAM), 컴퓨트(Lambda), 데이터베이스(DynamoDB), 그리고 프론트엔드 로직(JavaScript).

다음 마일스톤은 이 기반 위에서 계속 구축하는 것으로, 구체적으로는 Terraform을 사용한 인프라스트럭처 코드(IaC)를 깊이 파고들고 Docker/ECS를 활용한 컨테이너화 탐색입니다.

실제로 해보면서 배우고, 문제를 일으키고, 해결하는 클라우드 엔지니어를 찾고 계시다면—연락 주세요!

Live Site: https://aminetraibi.com
GitHub Repository: https://github.com/AmineTra/cloud-resume-challenge

Success

0 조회
Back to Blog

관련 글

더 보기 »

트라비고

Gemini와 함께 말하는 속도만큼 빠르게 여행하세요! 라이브 에이전트가 몰입형 스토리텔링 및 3D 내비게이션과 만나는 곳. 이 프로젝트는 Gemini Live Ag...에 진입하기 위해 만들어졌습니다.