TokyoAJ

도쿄아재

PYTHON 2025.07.11

Flask와 AWS S3를 활용한 파일 다이렉트 업로드

Flask와 AWS S3를 활용한 파일 업로드 시스템 구현 가이드

목차

  1. 소개
  2. 시스템 아키텍처
  3. 백엔드 구현 (Flask)
  4. 프론트엔드 구현 (HTML/JS)
  5. AWS S3 설정
  6. 보안 고려사항
  7. 개선 가능한 부분

1. 소개

이 프로젝트는 Flask 웹 프레임워크와 AWS S3를 활용하여 구현한 현대적인 파일 업로드 시스템입니다.

주요 기능

  1. 드래그 앤 드롭 파일 업로드
  2. 실시간 업로드 진행률 표시
  3. 파일 업로드 취소 기능
  4. S3 파일 삭제 기능
  5. 중복 파일 업로드 방지
  6. 프리사인드 URL을 통한 안전한 업로드

2. 시스템 아키텍처

+-------------------+ +-------------------+
| | | |
| Flask App | | Client-Side |
| | | Interface |
| - /get-presigned-url | - File Upload |
| - /delete-file |<------>| - Progress Bar |
| | | - Delete Button |
+-------------------+ +-------------------+
|
|
v
+-------------------+
| |
| AWS S3 |
| |
| - Store Files |
| - Generate URLs |
| - Delete Files |
| |
+-------------------+

3. 백엔드 구현 (Flask)

기본 설정 및 초기화

from flask import Flask, request, jsonify, render_template
import boto3

app = Flask(__name__)

# AWS S3 기본 설정
S3_BUCKET = "

CORS 설정

def configure_bucket_cors():
cors_configuration = {
'CORSRules': [{
'AllowedHeaders': ['*'],
'AllowedMethods': ['PUT', 'POST', 'GET'],
'AllowedOrigins': ['*'],
'ExposeHeaders': ['ETag']
}]
}
try:
s3.put_bucket_cors(Bucket=S3_BUCKET, CORSConfiguration=cors_configuration)
print("CORS configuration updated successfully")
except Exception as e:
print(f"Error configuring CORS: {e}")

주요 엔드포인트

메인 페이지 라우트

@app.route('/')
def index():
return render_template('/index.html')

프리사인드 URL 생성 엔드포인트

@app.route('/get-presigned-url', methods=['GET'])
def get_presigned_url():
file_name = request.args.get('file_name')
file_type = request.args.get('file_type')

try:
presigned_url = s3.generate_presigned_url(
'put_object',
Params={
'Bucket': S3_BUCKET,
'Key': file_name,
'ContentType': file_type
},
ExpiresIn=3600 # 1시간 유효
)
file_url = f"https://{S3_BUCKET}.s3.{S3_REGION}.

파일 삭제 엔드포인트

@app.route('/delete-file', methods=['DELETE'])
def delete_file():
file_name = request.args.get('file_name')
try:
s3.delete_object(
Bucket=S3_BUCKET,
Key=file_name
)
return jsonify({'success': True})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500

4. 프론트엔드 구현

HTML 구조

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>S3 Upload</title>
</head>
<body>
<div id="dropZone">
Drag & Drop files here or click to select files
</div>
<input type="file" id="fileInput" multiple />
<div id="progressList"></div>
</body>
</html>

CSS 스타일링

/* 드래그 앤 드롭 영역 스타일 */
#dropZone {
width: 100%;
height: 200px;
border: 2px dashed #ccc;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
transition: all 0.3s ease;
}

/* 드래그 오버 시 스타일 변경 */
#dropZone.dragover {
background-color: #e3f2fd;
border-color: #2196f3;
}

/* 진행 상태 컨테이너 스타일 */
.progress-container {
margin: 10px 0;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
position: relative;
min-height: 80px;
}

JavaScript 주요 기능

드래그 앤 드롭 이벤트 처리

// 드래그 앤 드롭 이벤트 리스너
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
});

// 드래그 오버 시 시각적 피드백
function highlight(e) {
dropZone.classList.add('dragover');
}

function unhighlight(e) {
dropZone.classList.remove('dragover');
}

파일 업로드 처리

function handleFiles(files) {
if (files.length === 0) return;
Array.from(files).forEach(file => {
if (!isFileAlreadyUploading(

단일 파일 업로드 구현

function uploadSingleFile(file) {
// 1. 프리사인드 URL 요청
fetch(`/get-presigned-url?file_name=${encodeURIComponent(

5. AWS S3 설정

버킷 정책

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::

CORS 설정

[
{
"AllowedHeaders": [
"Authorization",
"Content-Type",
"x-amz-date",
"x-amz-content-sha256",
"content-type",
"content-md5",
"x-amz-acl"
],
"AllowedMethods": [
"GET",
"PUT",
"POST",
"DELETE",
"HEAD"
],
"AllowedOrigins": [
"

6. 보안 고려사항

필수 보안 설정

  1. AWS 인증 정보 관리환경 변수 사용
  2. 시스템 환경 변수 설정
  3. .env 파일 활용
  4. CORS 설정특정 도메인으로 제한
  5. 필요한 HTTP 메서드만 허용
  6. 프리사인드 URL적절한 만료 시간 설정
  7. 파일 크기 제한 구현

보안 강화 코드 예시

# 환경 변수 사용
from dotenv import load_dotenv
import os

load_dotenv()

AWS_ACCESS_KEY = os.getenv('AWS_ACCESS_KEY')
AWS_SECRET_KEY = os.getenv('AWS_SECRET_KEY')

# 파일 크기 제한
@app.route('/get-presigned-url', methods=['GET'])
def get_presigned_url():
file_size = request.args.get('file_size', type=int)
# 100MB 제한
if file_size > 100 * 1024 * 1024:
return jsonify({'error': 'File size too large'}), 400

7. 개선 가능한 부분

파일 크기 제한

function validateFileSize(file) {
const maxSize = 100 * 1024 * 1024; // 100MB
if (file.size > maxSize) {
alert('File size exceeds limit');
return false;
}
return true;
}

파일 형식 검증

function validateFileType(file) {
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!allowedTypes.includes(file.type)) {
alert('File type not supported');
return false;
}
return true;
}

에러 처리 강화

class S3UploadError(Exception):
pass

def handle_upload_error(e):
if isinstance(e, S3UploadError):
return jsonify({'error': str(e)}), 400
return jsonify({'error': 'Internal server error'}), 500

테스트 코드 작성

import unittest
from app import app

class TestS3Upload(unittest.TestCase):
def setUp(self):
self.client = app.test_client()
def test_presigned_url_generation(self):
response = self.client.get('/get-presigned-url?file_name=test.jpg&file_type=image/jpeg')
self.assertEqual(response.status_code, 200)
self.assertIn('upload_url', response.json)

결론

이 프로젝트는 Flask와 AWS S3를 활용하여 안정적이고 사용자 친화적인 파일 업로드 시스템을 구현한 좋은 예시입니다. 프리사인드 URL을 활용한 직접 업로드 방식은 서버 리소스를 효율적으로 사용하면서도 안전한 파일 업로드를 가능하게 합니다.

주요 장점

  1. 모듈화된 코드 구조
  2. 명확한 프론트엔드/백엔드 분리
  3. 확장 가능한 아키텍처
  4. 사용자 친화적 인터페이스

향후 개선 방향

  1. 이미지 리사이징 기능 추가
  2. 파일 버전 관리 시스템 구현
  3. 보안 설정 강화
  4. 성능 최적화

기술적 학습 포인트

  1. 프리사인드 URL의 활용서버 부하 분산
  2. 보안 강화
  3. 대용량 파일 처리
  4. 클라이언트 사이드 업로드XMLHttpRequest 활용
  5. 진행률 표시
  6. 에러 처리
  7. AWS S3 설정CORS 정책
  8. 버킷 정책
  9. 보안 관리

이 시스템은 실제 프로덕션 환경에서도 활용 가능한 수준의 안정성과 확장성을 제공하며, 다양한 웹 애플리케이션에 적용할 수 있는 범용적인 파일 업로드 솔루션입니다.

댓글