본문 바로가기
Programming/AWS

[AWS] Serverless Web 게시판 만들기 -2 (사진업로드) S3, DynamoDB

by soccerman 2021. 2. 17.
반응형

Serverless Web 게시판 만들기 1편의 후속작입니다.

soccer-programming.tistory.com/45

 

[AWS] Serverless Web 게시판 만들기 -1 Lambda, API Gateway, DynamoDB 활용

AWS 제품을 활용해 서버리스 웹 어플리케이션을 구현하기 시작한 사람들을 위해 글을 게시합니다. 구조는 아래와 같습니다. 구조를 간단하게 설명하면 1. DynamoDB에 특정 작업을 하는 Lambda함수를

soccer-programming.tistory.com

글과 함께 사진을 업로드하고 게시판에서 이를 보여주는 간단한 웹 어플리케이션을 만들어 보겠습니다.

 

docs.aws.amazon.com/ko_kr/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html

 

브라우저에서 Amazon S3에 사진 업로드 - 용 AWS SDK JavaScript

인증되지 않은 사용자의 액세스를 활성화하면 버킷과 해당 버킷의 모든 객체, 전 세계 모든 사람에게 쓰기 권한을 부여하게 됩니다. 이러한 보안 태세는 이 사례의 기본 목표에 초점을 맞추는

docs.aws.amazon.com

위의 AWS doc를 참고했습니다.

 

구조는 아래와 같습니다.


1. Cognito를 이용해 자격증명을 받고 S3에 이미지 파일을 올리고 파일 URL을 받습니다.

2. HTML 상의 게시글 Title(제목), Content(내용)와 파일 URL을 DB에 넣습니다.

3. DB에서 Title, Content, 파일 URL을 받아와 HTML 요소에 추가하여 웹상에서 보여줍니다.


(재구성)출처 : https://aws.amazon.com/ko/getting-started/hands-on/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito/


일단 이미지 파일을 저장할 S3 버킷을 만듭니다.

 

이후 생성된 버킷을 클릭하고 권한탭에서 버킷 정책부분을 아래와 같이 수정합니다.

이는 해당 버킷을 외부에서 읽는 것을 허용하는 것입니다. 아래에서 복사할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": "해당 버킷 arn/*"
        }
    ]
}
cs
CORS 편집도 아래와 같이 해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "HEAD",
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]
 
 
cs

이제 Cognito 자격증명을 생성해줍니다.

이후 아래의 풀생성 버튼을 눌러주고

unauthenticated 역할 부분의 정책문서 보기를 누른후 아래의 정책 부분을

아래의 정책으로 수정해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::본인의S3버킷이름/*",
                "arn:aws:s3:::본인의S3버킷이름"
            ]
        }
    ]
}
cs

 

그리고 허용 버튼을 눌러줍니다. 그리고 나오는 다음 화면에서

플랫폼을 JavaScript로 변경하고 AWS 자격증명 얻기 부분을 복사해놓습니다.

 

html 파일 외에 js 디렉토리 안에 자바스크립트 파일을 하나 만들고 아래의 코드를 넣어줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var albumBucketName = "본인S3버켓이름";
var bucketRegion = "본인S3버켓리전";
var IdentityPoolId = "방금 Cognito자격증명 만들면서 받았던 AWS 자격증명 IdentityPoolId";
 
AWS.config.update({
  region: bucketRegion,
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: IdentityPoolId
  })
});
 
var s3 = new AWS.S3({
  apiVersion: "2006-03-01",
  params: { Bucket: albumBucketName }
});
cs

지금까지 S3을 만들고 각종 권한을 설정하고, Cognito를 통해 만든 자격증명을 configure하는 js 파일을 만들었습니다.


현재는 API Gateway에서 GET 메소드만 만들었기 때문에 DynamoDB에서 데이터를 받아오기만 할 수 있습니다.

 

이제 POST 메소드를 추가한 후 Http 통신을 하는 자바스크립트 코드를 짜서 Dynamo DB에 항목을 넣을 것입니다.

 

API Gateway 콘솔로 이동하여 기존 리소스에서 메소드 생성을 눌러 POST 메소드를 추가합니다.

Lambda 프록시 통합 사용에 체크하고 기존 GET 메소드에 연결되었던 동일한 Lambda함수를 선택하고 저장을 합니다.

이후 API 배포를 해줍니다.

 

이제 HTML과 JavaScript 코드를 아래와 같이 추가해줍니다. (파일 이름은 특별한 의미가 없습니다)

s3_photoExample_copy.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
var albumBucketName = "---------------------";
var bucketRegion = "-------------------------";
var IdentityPoolId = "-----------------------";
 
AWS.config.update({
  region: bucketRegion,
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: IdentityPoolId
  })
});
 
var s3 = new AWS.S3({
  apiVersion: "2006-03-01",
  params: { Bucket: albumBucketName }
});
 
//db에 정보 올리는 함수
function upload_to_db(img_location) {
    var article_id = document.querySelector("#id").value;
    var article_title = document.querySelector("#title").value;
    var article_content = document.querySelector("#content").value;
 
    var Item = {
        'article_id': article_id,
        'title': article_title,
        'content': article_content,
        'img_source': img_location,
    }
    console.log(Item);
 
    const URL = "본인 API GATEWAY 엔드포인트";
 
    fetch(URL, {
        method: "POST",
        headers: {
            'Accept''application/json'
        },
        body: JSON.stringify({
            "TableName""simple_board",
            Item
        })
    }).then(resp => console.log(resp))
        .catch(err => console.log(err))
}
 
function add_article_with_photo(albumName) {
    var files = document.getElementById("article_image").files;
    if (!files.length) {
        return alert("Please choose a file to upload first.");
    }
    var file = files[0];
    var fileName = file.name;
    var albumPhotosKey = encodeURIComponent(albumName) + "/";
    var albumPhotosKey = albumName + "/";
 
    var photoKey = albumPhotosKey + fileName;
 
    // Use S3 ManagedUpload class as it supports multipart uploads
    var upload = new AWS.S3.ManagedUpload({
        params: {
        Bucket: albumBucketName,
        Key: photoKey,
        Body: file
        }
    });
 
    var promise = upload.promise();
 
    let img_location;
 
    promise.then(
        function(data) {
        //이미지 파일을 올리고 URL을 받아옴
        img_location = JSON.stringify(data.Location).replaceAll("\"","");
        // console.log(img_location);
        
        upload_to_db(img_location);
 
        return alert("Successfully uploaded photo.");;
        },
        function(err) {
            console.log(err);
        return alert("There was an error uploading your photo: ", err.message);
        }
    );
    }
cs

s3uploadtest.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Article_Add</title>
        <style>
        </style>
            <!--켜지면 get() 함수를 실행함.  -->
            <body onLoad="get()">
                <div id="articles">
            
                </div>
 
        <script src="https://sdk.amazonaws.com/js/aws-sdk-2.283.1.min.js"></script>
        <script src="./js/s3_photoExample_copy.js"></script>
        
        <script>
            /* HTML노드 추가 함수 */
            function createNode(element){
                return document.createElement(element);
            }
            /* HTML노드에 자녀 추가 함수 */
            function append(parent, el){
                return parent.appendChild(el);
            }
            /* CSS 속성 추가 함수 */
            function addStyle(styles) { 
              
              /* Create style element */ 
              var css = document.createElement('style'); 
              css.type = 'text/css'
         
              if (css.styleSheet)  
                  css.styleSheet.cssText += styles; 
              else  
                  css.appendChild(document.createTextNode(styles)); 
                
              /* Append style to the head element */ 
              document.getElementsByTagName("head")[0].appendChild(css); 
          }
          var elem = document.getElementById('articles');
            /* API 게이트웨이로 매물리스트 정보 가져오는 함수 */ 
            const URL = "본인 API GATEWAY 엔드포인트";
            function get(){
                fetch(URL, {
                    method: "GET",
                    headers: {
                        'Accept''application/json'
                    }
                }).then(resp => resp.json())
                .then(function(data){
                    let article_arr = data.Items;
                    console.log(data.Items);
                    return article_arr.map(function(article_indiv){
                        let li = createNode('li');
                        let img = createNode('img');
                        let span = createNode('span');
                        
                        // console.log(article_indiv.img_source);
                        img.src = article_indiv.img_source;
                        span.innerHTML = article_indiv.title +" - " + article_indiv.content;
 
                        append(li,img);
                        append(li,span);
                        append(elem,li);
 
                        var styles = 'div#articles > li > img { width:200px; height:100px }';
                        addStyle(styles);
 
                    })
                })
                .catch(err => console.log(err))
            }
            
            function submitToAPI(e){
                e.preventDefault();
                add_article_with_photo('images');
             }
        </script>
 
    </head>
 
    <body>
        <div class="Top">
  
        </div>
        <div style="text-align: center;"><h1>글 등록 페이지</h1></div>
 
        <form name="article_add_form" method="POST">
            <table width="940" style="padding:5px 0 5px 0; ">
               <tr height="2"><td colspan="2"></td></tr>
               <tr>
                  <th>Id</th>
                  <td><input type="text" id="id" required></td>
               </tr>
               <tr>
                  <th>Title</th>
                  <td><input type="text" id="title" required></td>
                </tr>
                <tr>
                    <th>Content</th>
                    <td><input type="text" id="content" required></td>
                  </tr>>
                  <tr>
                    <th>사진</th>
                    <td><input type="file" id="article_image" name="filename"></td>
                  </tr>
                    <tr>
                      <td colspan="3" style="text-align: center;">
                        <button type="button" onclick="submitToAPI(event)"> 등록 </button>
                        <input type="reset" value="취소">
                     </td>
                    </table>
                   </form>
 
        </div>
 
    </body>
 
    <head>
 
    </head>
 
</html>
 
cs

html 파일을 키고 form에 값과 사진을 넣고 등록을 누르면

사진과 함께 새로운 게시글이 추가되었고

DynamoDB 테이블에서도 항목이 추가된 것을 확인할 수 있다.


이상 AWS Lambda, API Gateway, Dynamo DB를 활용한 간단한 서버리스 게시판 만들기 예제를 끝내겠습니다.

 

글에 실수가 있더라도 귀엽게 봐주시면 감사하겠습니다.

 

질문은 댓글 남겨주시면 빠른 시일 내로 답변 드리겠습니다. 감사합니다.

반응형

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

[AWS] Serverless Web 게시판 만들기 -1 Lambda, API Gateway, DynamoDB 활용  (4) 2021.02.07
[AWS] EBS 추가연결 실습  (0) 2020.05.09
[AWS] EBS 란?  (0) 2020.05.09
[AWS] RDS 란?  (0) 2020.04.16
[AWS] S3 란?  (0) 2020.04.14

댓글