본문 바로가기
Web Study/Next.js

코딩애플 - AWS Elastic Beanstalk, S3

by 쿠리의일상 2023. 7. 5.

배포하기에 앞서

1. AWS 라면 EC2 가 제일 유명하지만...

EC2는 가상 서버(인스턴스)를 빌리는 상품으로, 숙련자라면 커스터마이징이 가능하고 빠르기 때문에

EC2를 사용하는 것이 맞지만 편하고 쉽게 배포하려면 AWS의 Elastic Beanstalk 을 사용하는 것을 추천

 

2. AWS 의 Elastic Beanstalk 이란

코드만 올리면 알아서 EC2 인스턴스 빌리고 npm install 한 뒤 npm run start 까지 실행해주며,

무료로 도메인 연결과 24시간 서버 구동이 가능한 상품이다.

유저가 훗날 많아지면 확장도 쉬워지고 버전관리도 가능하다고 한다. 그러므로 사용자는 코드에 집중하면 되는 것

구글 클라우드에 App Engine 이라는 상품도 비슷한 역할을 해준다고 한다.

 

3. Vercel

Next.js 는 Vercel이 만들고 관리했기 때문에 Vercel 에서 배포가 매우 쉽다고 한다.

Github repo 에 코드를 올릴 때마다 자동으로 Elastic Beanstalk 과 유사하게 배포해준다고 한다.

그리고 월 100gb 트래픽까지 무료계정으로 가능하므로 포트폴리오용으로 편리하고 좋다고 한다. (다만 서버에 하드디스크가 없음)

https://vercel.com/

 

Vercel: Develop. Preview. Ship. For the best frontend teams

Vercel's frontend cloud gives developers the frameworks, workflows, and infrastructure to build a faster, more personalized Web.

vercel.com

 

배포 전 체크사항

배포해줄 가상 서버에도 mongodb에 접속을 해야 데이터를 꺼낼 수 있다.

즉, 몽고DB의 Network access 메뉴에서 접속 가능 IP를 0.0.0.0 으로 바꿔준다.

혹 보안을 높이고 싶다면 AWS VPC 로 mongodb atlas 연결하면 된다고 한다.

 

배포 순서

  1. 빌드 - npm run build
  2. zip 압축 - 프로젝트 폴더 안에 있는 파일들(node_modules 폴더 제외) 특히 .next 폴더를 포함한 내용을 압축
  3. AWS 접속 후 서울 리전으로 
  4. IAM 역할(aws-elasticbeanstalk-ec2-role) 만들기 - elastic beanstalk ec2 를 마음대로 사용할 수 있는 계정
  5. 설정 (버그 때문에 자동으로 안되는 경우)
    1. 신뢰할 수 있는 엔터티 AWS 서비스, 사용례는 EC2
    2. 권한 추가 메뉴에선 AWSElasticBeanstalkWebTier, AWSElasticBeanstalkWorkerTier, AWSElasticBeanstalkMulticontainerDocker 선택
  6. Elastic Beanstalk 시작하기
    1. 플랫폼 : nodejs 18버전
    2. 프리셋 : 단일 인스턴스
    3. 버전레이블 : version 1.0
    4. 로컬파일에 위에서 만든 zip 압축 파일 넣어줌
    5. 기존 서비스 역할 사용 > aws-elasticbeanstalk-service-role
    6. EC2 인스턴스 프로파일 > aws-elasticbeanstalk-ec2-role
    7. t2.micro
  7. 도메인 접속 - 배포 완료

 

배포는 됐지만 로그인/회원가입이 안된다면

Nextauth 와 소셜로그인 세팅을 변경해줘야 한다.

원래 OAuth 용 앱 만들었던건 개발용 url, 즉 localhost:3000 에 가능한 것이므로 위에서 할당 받은 도메인 주소로 바꿔줘야 한다.

처음부터 새로운 OAuth 앱을 만들어서 개발용과 배포용 두개씩 만들어 놓으면 편하다.

 

다음은 NEXTAUTH_URL 환경 변수도 설정해줘야 한다.

NEXTAUTH_URL = 배포한 사이트 주소 를 .env 에 추가해줘야 하는데, 재배포 시 다시 위의 소스코드zip 을 올려야하므로

AWS CloudShell(AWS 사이트 왼쪽 하단)에 접속하여

eb use 환경이름 // Elastic Beanstalk 에 접속하여 환경 탭으로 접속하면 확인 가능
eb setenv NEXTAUTH_URL=배포한사이트주소

처럼 써주면 됨. 다만 버그가 많아서 안될 가능성이 크므로 소스파일 zip 을 지향

 

사이트 업데이트 시

업로드 및 배포 버튼 눌러서 또 zip 파일로 묶어서 업로드하면 되지만 요금이 나올 수 있음

 

이미지 업로드

AWS S3

AWS 를 가입하면 S3가 1년간 무료로 5gb 만큼 이용 가능

AWS S3보다 쉽게 사용하려면 firebase 를 이용한다.(평생 5gb 무료)

 

  1. S3 접속
  2. 버킷 만들기 - 버킷은 저장공간과 동일한 의미
  3. 버킷의 퍼블릭 액세스 차단 설정은 개발 시에는 아래처럼

4. 버킷의 보안을 높이고자 하면, 버킷 > 권한 > 버킷 정책 편집으로 관리자와 일반유저의 권한을 정의해준다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::버킷명/*"
        },
        {
            "Sid": "2",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::AWS계정ID:root"
            },
            "Action": [
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::버킷명/*"
        }
    ]
}
  • 자료 읽기 -  s3:GetObject
  • 쓰기 - s3:PutObject
  • 삭제 - s3:DeleteObject

5. CORS 설정

버킷 안의 파일들을 읽기,쓰기,삭제할 수 있는지 CORS 설정하는 부분

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

6. Access 키 발급

액세스 키와 비밀 키 2개 세트를 코딩할 때 사용

 

7. 업로드할 이미지를 고르면 미리볼 수 있게끔 처리

Presigned URL

브라우저에서 서버를 거치지 않고 직접 S3에 업로드가 가능(서버 부하⬇️)

  • input 태그에 이미지를 고르는 순간 서버에 GET 요청
  • 서버는 유효한 요청인지 확인하고 Presigned url 을 만들어서 유저 브라우저로 응답
  • 브라우저에선 presigned url 을 이용하여 S3로 POST 요청해서 이미지를 보냄
  • 업로드 성공 시 해당 url 을 img 태그에 넣어 업로드된걸로 보여줌

프론트측에서 file 을 입력 받으면 e.target.files[0] 에 업로드시킨 파일이 들어간다.

파일의 이름으로 encodeURIComponent() 함수를 사용하여 파일명을 분리시키고 /api/post/image url에 파일명을 쿼리스트링으로 보내준다.

<input type="file" accept="image/*" onChange={ 
async (e)=>{
  let file = e.target.files[0]
  let filename = encodeURIComponent(file.name)
  let res = await fetch('/api/post/image?file=' + filename)
  res = await res.json()
}
} />
<img />

서버에서 presigned url 발급해서 보내주기

먼저 npm install aws-sdk 를 설치하여 AWS를 다룰 수 있는 라이브러리를 설치한다.

aws.config.update 안에 정보를 채워줘야 한다.

import aws from 'aws-sdk'
export default async function handler(request, response){
    aws.config.update({
      accessKeyId: process.env.REACT_APP_ACCESS_KEY,
      secretAccessKey: process.env.REACT_APP_SECRET_KEY,
      region: 'ap-northeast-2',
      signatureVersion: 'v4',
    })

    const s3 = new aws.S3();
    const url = await s3.createPresignedPost({
      Bucket: process.env.REACT_APP_BUCKET_NAME,
      Fields: { key : request.query.file },
      Expires: 60, // seconds
      Conditions: [
        ['content-length-range', 0, 1048576], //파일용량 1MB 까지 제한
      ],
    })

    response.status(200).json(url)
}
  • aws-sdk 로 S3() 를 new 해준다음, createPresignedPost() 메서드를 사용해주면 PresignedURL 을 발급받을 수 있다.
  • Fields 에 들어가는 key는 유저가 보낸 쿼리스트링의 파일명을 넣어주면 된다.
  • 그밖에 업로드 가능한 용량(content-length-range)과 제한시간(Expries) 설정도 가능

 

.env에는 위에 사용한 값을 넣어주고

REACT_APP_ACCESS_KEY=액세스키
REACT_APP_SECRET_KEY=액세스키비번
REACT_APP_BUCKET_NAME=버킷명

 

 

'use client'
import { useState } from "react";

export default function Write(){ 
  let [src, setSrc] = useState('')

  return (
    <div className="p-20">
      <h4>글작성</h4>
        <form action="/api/post/new" method="POST"> 
        <input name="title" placeholder="글제목"/> 
        <input name="content" placeholder="글내용"/> 
        <button type="submit">전송</button> 
      </form>

      <input type="file" accept="image/*" onChange={
        async (e) => {
          let file = e.target.files[0]
          let filename = encodeURIComponent(file.name)
          let res = await fetch('/api/post/image?file=' + filename)
          res = await res.json()
          
          //S3 업로드
          const formData = new FormData()
          Object.entries({ ...res.fields, file }).forEach(([key, value]) => {
            formData.append(key, value)
          })
          let result = await fetch(res.url, {
            method: 'POST',
            body: formData,
          })
          console.log(result)

          if (result.ok) {
            setSrc(result.url + '/' + filename)
          } else {
            console.log('실패')
          }
          
        }
      } />
      <img src={src} />

    </div>
  )
}

위처럼 받아준 res 값을 활용하여 S3에 업로드 해준다.

이때 form 태그 대신, new FormData() 를 사용해주고 데이터를 넣어줄 수도 있다.

result url 뒤에 파일이름을 붙여주면 업로드된 이미지의 경로가 되므로 해당 경로를 img src에 넣어주면 된다. (useState 로 관리)

 

AWS 사용을 간단하게 해보았다..😂