본문 바로가기
Study/AWS

AWS(6)-S3에 파일 upload(Django, Spring Boot, react)

by 왕방개 2024. 4. 17.

Why 모델은 Tensorflow 를 사용하는지?

 

Tensorflow 은 JavaScript 모델 단에 배포할 수 있어서 서버입장에서 부담을 덜 수 있음.Pytorch는 자유도가 높아 서버 측면에서만 모델 배포 가능

 

1.파일 업로드가 가능한 S3 버킷 생성

=>업로드가 되는 파일은 웹 애플리케이션에서 업로드되는 파일이 주였지만 최근에는 Data Lake 구성에 S3 가 많이 사용됩니다.

차후에 Serverless(서버가 직접 구축할 이유가 없음) 인 Lambda 같은 기능을 같이 사용하면 파일이 업로드되는 이벤트가 발생하면 메세지를 받아서 특정 작업을 수행하도록 할 수 있습니다.

 

1)버킷 생성

=>s3 서비스에서 [버킷 만들기]를 클릭

 

=>버킷 이름 설정

버킷의 이름은 리전에서 유일무이 해야 합니다

버킷의 이름은 나중에 웹 서비스에서 도메인이 될 수 있기 때문

 

=>객체 소유권 설정

기본 설정을 하게 되면 현재 로그인 한 유저만 사용이 가능하고 [ACL 활성화됨]을 체크하면 다른 유저들 그리고 응용 프로그램에서 파일을 업로도하고 다운로드 받을 수 있습니다

 

 

=>퍼블릭 엣게스 차단 설정

외부에서 접근하지 않을거라면 체크를 해두지만 응용 프로그램에서 사용하거나 데이터 레이크 구성에 이용할거라면 차단 설정을 해제해야 합니다

 

=>버킷 버전 관리

버킷의 내용을 백업할 때 버전을 이용해서 과거와 현재 데이터를 구분할 수 있도록 해주는 기능

버전 관리를 하지 않을 거라면 업로드 되는 파일에 날짜나 버전을 명시해주는 것이 좋습니다.

데이터레이크를 구성할 떄는 일자 또는 월별로 디렉토리를 만들어서 데이터를 쌓습니다.

 

=>이렇게 만든 버킷은 관리 콘솔에서만 사용 가능하고 브라우저나 응용 프로그램에서는 사용할 수 없습니다.

 

2)버킷을 외부에서 사용할 수 있도록 설정

=>버킷에 수행할 작업을 설정: 버킷의 세부 정보에 들어가서 [권한] ->[버킷정책] 정보의 편집을 눌러서 작성

-  Resource 부분의 버킷 이름은 자신의 버킷으로 수정

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicListGet",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:List*",
                "s3:Get*",
                "s3:Put*",
                "s3:Delete*"
            ],

            "Resource": [
                "arn:aws:s3:::Bucket이름",
                "arn:aws:s3:::Bucket이름/*"
            ]
        }
    ]
}

자신의 버킷이름에 맞춰서 밑에를 수정!

 

=>[권한] 탭에서 [CORS] 설정으로 이동해서 편집을 누르고 정책 추가

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": ["Access-Control-Allow-Origin"]
    }
]

 

 

2.버킷 사용 방법

1)관리 콘솔에서 사용

 

2)응용 프로그램에서 사용

=>python이나 spring boot 같은 프로그래밍 언어나 프레임워크를 이용해서 만든 Application에서 사용

 

3)AWS CLI 를 이용해서 사용

 

 

3. Django 에서 파일 업로드 구현

=>배포할 application이라면 가상 환경을 만들고 애플리케이션을 구현하고 배포하지 않을거라면 가상 환경을 만들지 않아도 됩니다.

 

1)django app을 생성

=>패키지 설치

django

mysqlclient: 데이터베이스 연동을 위해서

-Pillow:이미지 처리 필요

-django-bootstrap4: 템플릿을 이용해서 출력할 때 bootstrap을 편리하게 적용하기 위해서 필요한데 클라이언트 애플리케이션을 별도로 구현하는 경우 불필요

bootstrap은 반응형 웹 애플리케이션(디바이스나 브라우저의 크기에 따라 CSS 설정을 변경해서 동일한 컨텐츠를 사용할 수 있도록 해주는 것)을 쉽게 구현해주는 javascript library

 

=>프로젝트 생성

django-admin startproject 프로젝트이름 .

 

=>애플리케이션 생성

python manage.py startapp 애플리케이션이름

 

 

 

2)데이터베이스 및 환경 설정 - project 의 settings.py 파일을 수정

데이터베이스 사용
여러 명이 같이 사용하고자 하면 데이터베이스는 로컬에 설치되어 있으면 안되고 배포할때도 문제가 발생

-로컬 컴퓨터에 데이터베이스를 설치하고 로컬 컴퓨터를 포트 포워딩을 이용해서 고정 IP를 갖도록 해서 사용하는 방식

-AWS 에서 EC2 인스턴스를 생성하고 그 안에 데이터베이스를 설치하고 외부에서 접속이 가능하도록 설정해서 사용:public IP 가 기본적으로 고정이지만 재부팅을 하게되면 IP가 변경될 수 있으므로 이 경우는 되도록 Elastic IP를 부여받아서 사용하는 것이 좋습니다

-AWS 에서 제공하는 RDS 같은 DataBase 서비스를 이용하는 방식
가장 편리하기는 한데 비용이 추가되고 Document DB 나 Dynamo DB 는 외부에서 접속하려면 EC2 인스턴스가 1개 필요

*로 수정

 

밑에 애플리케이션이름이랑 bootstrap4 추가

 

데이터베이스 내용 변경

 

시간 설정

 

=>화면 출력을 서버에서 만들어 줄것이라면 애플리케이션 디렉토리에 templates 디렉토리 생성

=>현재 프로젝트에 파일을 업로드 할 것이라면 프로젝트 디렉토리에 media 디렉토리를 생성

 

=>models.py 파일에 테이블과 연동되는 모델(Entity)클래스를 생성:데이터베이스에 애플리케이션이름_모델클래스 이름으로 만들어진 테이블과 연동이 됩니다.

from django.db import models

class FileUpload(models.Model):
  title = models.TextField(max_length=40,null=True)
  #파일의 자료형은 ImageField 로 설정
  imgfile = models.ImageField(null=True,upload_to="",blank=True)
  content = models.TextField()
  
  def __str__(self):
    return self.title

 

 

=>마이그레이션 수행 -변경된 내용을 데이터베이스에 적용

 

python manage.py makemigrations

 

python manage.py migrate

 

=>파일을 데이터베이스에 저장하는 방법

-파일의 내용을 데이터베이스에 저장(LOB)하는 방법

별도의 파일 서버가 없어도 된다는 장점이 있지만 데이터베이스 부하가 커지고 파일의 형식을 같이 저장해야 합니다

 

-파일의 경로를 데이터베이스에 저장하는 방법

별도의 파일 서버 (S3와 같은)을 만들고 그 서버에 저장된 파일의 경로를 저장하는 방법인데 이경우 파일이름에 주의해야 합니다.

일반적인 서버는 파일의 이름을 key로 하게 되는데 파일의 이름이 겹치면 이전 파일이 없어지고 새로운 파일로 업데이트 됩니다. 디렉토리 단위로 구분하거나 파일 이름에 현재 시간 또는 ID 또는 UUID를 추가해서 중복되지 않는 이름을 만들어사 사용합니다.

 

 

3)파일 업로드 구현

=>애플리케이션 디렉토리에 모델의 내용을 기반으로 입력받는 폼을 위한 파일을 생성 - forms.py

from django.forms import ModelForm
from .models import FileUpload
class FileUploadForm(ModelForm):
    class Meta:
        model = FileUpload
        fields = ['title', 'imgfile', 'content']

 

=>사용자의 요청을 처리하는 코드를 작성 - 애플리케이션의 views.py(Spring Boot 에서는 Service)

from django.shortcuts import render, redirect

from .forms import FileUploadForm
from .models import FileUpload

#요청을 처리하는 함수
def fileUpload(request):
    #하나의 요청 처리 메서드에서 요청 방식에 따라 처리
    if request.method == "GET":
        fileuploadForm = FileUploadForm

        context = {
            'fileuploadForm': fileuploadForm,
        }
        #fileupload.html 파일을 출력
        return render(request, 'fileupload.html', context)
    elif request.method == "POST":
        #파라미터 읽기
        title = request.POST["title"]
        content = request.POST['content']
        img = request.FILES['imgfile']

        #데이터베이스에 저장할 객체를 생성
        fileupload = FileUpload(
            title = title,
            content = content,
            imgfile = img
        )

        #데이터베이스에 저장
        fileupload.save() #ORM - SQL을 사용하지 않고 객체를 이용해서 데이터베이스 작업
        return redirect('fileupload') #작업이 끝나면 fileupload 요청을 다시 수행

 

=>출력 파일의 공통된 요소을 가지고 있는 base.html 파일을 templates 디렉토리에 생성하고 작성

{% load static %}
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</head>
<body>
    {% block content %}
    {% endblock %}
</body>
</html>

 

=>실제 파일 업로드 화면으로 사용될 fileupload.html 파일을 templates 디렉토리에 생성하고 작성

{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% block content %}
<div class="container">
    <form method="POST" action="{% url 'fileupload' %}" enctype="multipart/form-data">
        {% csrf_token %}
        {% bootstrap_form fileuploadForm %}
        <input type="submit" class="btn btn-primary col-12" value="제출">
    </form>
</div>
{% endblock%}

 

=>프로젝트의 urls.py 파일을 수정해서 요청이 오면 처리 메서드를 호출하도록 변경

from django.contrib import admin
from django.urls import path


from fileupload import views

urlpatterns = [
    path("admin/", admin.site.urls),
    path('fileupload/', views.fileUpload, name="fileupload")
]

 

=>프로젝트의 settings.py 파일에 파일을 저장하기 위한 옵션을 추가

#현재 프로젝트 내부에 파일을 저장하고자 할 때 사용하는 설정
import os
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

 

=>프로젝트를 실행하고 fileupload 요청을 전송해서 파일 업로드를 확인

=>이렇게 하게 되면 파일이 업로드될 때 마다 프로젝트의 크기가 커지게 되서 나중에는 리소스 와 코드의 분리가 어려워집니다.

 

4)S3 버킷에 파일이 저장되는 파일 업로드 구현

 

=>준비 사항
- 외부에서 파일을 업로드 할 수 있는 버킷이 생성
  버킷의 이름: adammainbucket
  버킷의 리전: ap-northeast-2

- S3 버킷에 접근할 수 있는 권한을 가진 user의 access key 와 secret access key 가 필요
  IAM 서비스에서 유저를 생성하고 s3FullAccess 권한을 부여한 후 엑세스 키를 발급받으면 됩니다.
  사용자 생성을 클릭해서 사용자 이름을 입력하고 [직접 정책 연결]을 클릭한 후 s3로 검색을 해서 AmazonS3FullAccess 권한을 부여해서 유저를 생성
  생성된 유저에서 엑세스 키 만들기를 클릭한 후 CLI를 선택해서 키를 생성하고 csv 파일을 다운로드

 

- 패키지 설치
  pip install boto3
  pip install django-storages

 

=>settings.py 파일에 AWS 설정을 추가하고 변경

# AWS Setting
AWS_REGION = 'ap-northeast-2' #AWS서버의 지역
AWS_STORAGE_BUCKET_NAME = 'adammainbucket' #생성한 Bucket 이름
AWS_ACCESS_KEY_ID = 'AKIAZKP7ENHO3UNM2ZM7' #액서스 키 ID
AWS_SECRET_ACCESS_KEY = 'GBiqVTvkYQbIs6RQzFqPwMo9NHPawsebwe5zxMEF' #액서스 키 PW
#Bucket이름.s3.AWS서버지역.amazonaws.com 형식
AWS_S3_CUSTOM_DOMAIN = '%s.s3.%s.amazonaws.com' % (AWS_STORAGE_BUCKET_NAME, AWS_REGION)
# Static Setting
STATIC_URL = "https://%s/static/" % AWS_S3_CUSTOM_DOMAIN
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

# Media Setting 
MEDIA_URL = "https://%s/meida/" % AWS_S3_CUSTOM_DOMAIN
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

 

=>정보 설정이 제대로 되었는지 확인: S3 에 static 파일이 업로드 되고 admin 이라는 디렉토리가 생성

python manage.py collectstatic

 

- admin 이라는 디렉토리가 만들어져 있지 않으면 AWS 버킷 정보를 수정

=>views.py 파일의 내용을 수정해서 파일을 업로드를 하면 S3에 저장되도록 수정: 이 부분을 작업하지 않아도 파일 업로드는 성공하는데 이 코드를 작성하면 기존파일이름 뒤에 uuid가 추가되서 파일 이름이 만들어 집니다.

from django.shortcuts import render, redirect

from .forms import FileUploadForm
from .models import FileUpload

import uuid

#요청을 처리하는 함수
def fileUpload(request):
    #하나의 요청 처리 메서드에서 요청 방식에 따라 처리
    if request.method == "GET":
        fileuploadForm = FileUploadForm

        context = {
            'fileuploadForm': fileuploadForm,
        }
        #fileupload.html 파일을 출력
        return render(request, 'fileupload.html', context)
    elif request.method == "POST":
        #파라미터 읽기
        title = request.POST["title"]
        content = request.POST['content']
        img = request.FILES['imgfile']

        #파일 업로드되는 이름을 수정
        idx = img.name.rfind(".") #.의 마지막 위치 찾기
        #확장자가 없으면 uuid를 뒤에 추가하고 확장자가 있는 경우는 확장자 앞에 추가
        #이 구문이 없어도 업로드 가능한데 중복되는 이름을 회피하기 위해서 수행
        if idx != -1:
            img.name = img.name[:idx] + str(uuid.uuid4()) + img.name[idx:]
        else:
            img.name = img.name + str(uuid.uuid4())

        #데이터베이스에 저장할 객체를 생성
        fileupload = FileUpload(
            title = title,
            content = content,
            imgfile = img
        )

        #데이터베이스에 저장
        fileupload.save() #ORM - SQL을 사용하지 않고 객체를 이용해서 데이터베이스 작업
        return redirect('fileupload') #작업이 끝나면 fileupload 요청을 다시 수행

 

5)데이터베이스 정보를 이용해서 업로드 된 파일을 다운로드 받을 수 있는 화면을 생성

 

=>views.py 파일의 파일 업로드 성공 후 리다이렉트 되는 URL을 수정

return redirect('/') #작업이 끝나면 / 요청을 수행

 

=>views.py 파일에 / 요청이 오면 처리하는 메서드를 추가

def main(request):
    #테이블의 모든 데이터 가져오기
    pictures = FileUpload.objects.all()

    #뷰에 전달해서 출력
    return render(request, "main.html", {'result': pictures})

 

 

=>애플리케이션의 templates 디렉토리에 main.html 파일을 생성하고 작성

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width={device-width}, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    {% if result %}
        {% for picture in result %}
            <img src=
            "https://adammainbucket.s3.ap-northeast-2.amazonaws.com/{{picture.imgfile}}" />
        {% endfor %}
    {% endif %}

</body>
</html>

 

=>urls.py 파일에 url 과 요청 처리 메서드 연결하는 부분을 추가

urlpatterns = [
    path("admin/", admin.site.urls),
    path('fileupload/', views.fileUpload, name="fileupload"),
    path('', views.main, name="main")
]

 

=>서버를 구동하고 기본 요청을 했을 때 업로드 한 이미지가 보이는지 확인

=>기본 요청을 처리하는 함수를 수정하면 뷰로 출력하는 것이 아니고 JSON으로 출력이 가능합니다

from django.http import JsonResponse

def main(request):
    #테이블의 모든 데이터 가져오기
    pictures = FileUpload.objects.all()

    #뷰에 전달해서 출력
    #return render(request, "main.html", {'result': pictures})
    return JsonResponse({"list": list(pictures.values())})

=>파일을 다운로드 것은 실제 파일의 URL을 설정해주면 됩니다.
지금의 경우는 데이터베이스에서는 파일의 이름만 보관하고 앞에 URL은 공통된 부분이므로 직접 설정을 한 것 입니다.

'Study > AWS' 카테고리의 다른 글

Server Application 배포-EC2 활용  (0) 2024.05.02
AWS(7)-S3 에 Spring Boot 프로젝트 업로드  (0) 2024.04.18
AWS(5) - DataBase  (0) 2024.04.16
AWS(4)  (0) 2024.04.15
AWS(3)  (0) 2024.04.13