PresignedUrl를 이용해 이미지 입력을 효율적으로 처리하기

임현규·2023년 6월 26일
0

Meca project 개발 일지

목록 보기
23/27
post-thumbnail

서버에서 이미지 입력을 직접 수행하는 것의 문제점

직접 이미지를 Spring 서버에 입력하는 것은 몇가지 문제점이 존재한다.

  1. 용량 및 대역폭 문제
  • 이미지는 일반적인 json과 같은 데이터에 비해 용량 사이즈가 크다. 그렇기에 직접 업로드하는 방식은 서버 용량도 많이 차지할 뿐만 아니라 대역폭에도 신경써야 한다. 클라우드 서비스를 사용하는 경우, 이미지 하나 때문에 서버의 비용이 증가할 수 있다.
  1. 확장성 문제
  • 위와 겹치는 내용도 있지만 용량 문제 때문에 계속해서 용량 한계에 도달하고 이미지 저장 때문에 서버를 확장해야 하는 문제가 발생할 수 있다.

스토리지 서버 활용하기

rest api를 구축하는 입장해서 이미지와 같은 큰 크기의 파일은 부담이 된다. 그래서 AWS S3(Simple Storage Service)와 같은 스토리지 서버를 구축해서 따로 보관하는 방식이 효과적일 수 있다. 스토리지 서버는 애초에 대용량 데이터를 처리하고 저장할 수 있는 능력이 있고 유연하게 대응할 수 있다. 뿐만 아니라 이 방법을 이용하는 것이 비용이 저렴할 수 있다.

S3와 Presigned URL

아무나 S3에 접근이 가능하다면 이것은 큰 문제를 발생할 우려가 있다. 그래서 s3 접근 권한을 가진 IAM 정보를 바탕으로 application server가 presigned url를 만들고 이를 client에게 준다. presigned url은 기한이 존재하는 s3 access 토큰이라 보면 된다.

클라이언트와 어플리케이션 서버 그리고 S3 간의 동작의 원리는 다음과 같다.

S3 Bucket 생성 및 권한 설정하기

많은 블로그에 잘 나와있지만 S3 버킷 생성은 간단하다. AWS S3에 접속한 이후

위와 같이 설정해서 생성하면 된다.

그러면 public이 가능한 형태의 S3 Bucket를 생성한다. 하지만 여기서 끝난 것이 아니다. 진짜 public으로 만들어서 접근하게 하려면 권한 설정이 필요하다

// 버킷 정책 설정
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "버킷ARN/*"
        }
    ]
}
// CORS 설정
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "x-amz-server-side-encryption",
            "x-amz-request-id",
            "x-amz-id-2"
        ],
        "MaxAgeSeconds": 3000
    }
]

설정후 권한 개요가 다음과 같이 나오면 public으로 사용할 수 있다.

IAM 추가하기

AWS에 사용하는 모든 자원은 실제 돈과 연결되기 때문에 root 계정을 이용해 접근하는 것은 보안상 위험할 수 있다. 그래서 접근이 제한된 계정을 생성해서 사용할 수 있는데 이를 AWS의 IAM 서비스를 통해 할 수 있다.

우리는 application server에서 S3에 대한 서비스를 지원하기 위해 접근하기위한 계정이 필요한데 S3에만 access 할 수 있는 계정을 생성해서 활용할 것이다.

IAM에 사용자 탭에 들어가면 우측 상단에 사용자 추가 버튼이 있다. 이를 클릭해주자.

그럼 권한 설정 탭이 나오는데 이때 직접 권한 설정을 누르고 S3 full-access를 선택한다

사실 S3 full access 보다는 용도에 맞게 최대한 권한을 축소해서 제공해주는게 제일 좋지만 프로젝트 편의상 s3 full access로 진행하겠다.

IAM으로 관리하는 계정이 많다면 태그를 추가할수도 있다.

사용자를 생성한후 해당 계정 정보에 들어가서 보안 자격 증명 탭을 클릭한다.

거기서 엑세스 키 생성을 누르면 다음과 같은 창이 뜬다.

그러면 최종적으로 AccessKey와 SecretKey를 보내줄 것이다.

secretKey의 경우 이번 뺴고 다시 보는 것이 불가능하기 때문에 저장하고 싶다면 .csv 파일로 다운받고 안전한 곳에 보관한다.

Application Server에서 S3에 접근하기 위한 기초 작업

우선 build.gradle에 다음과 같이 의존성을 추가한다

    // aws S3
    implementation 'com.amazonaws:aws-java-sdk-s3:1.12.429'
    testImplementation 'io.findify:s3mock_2.13:0.2.6'

첫번쨰는 아마존에서 제공하는 sdk, 2번째는 s3서버를 mocking해서 테스트할 때 사용한다. 동작은 첫번째만 등록해도 된다.

이제 S3 관련 활동을 위해서 아까 만들어둔 IAM의 access key와 secret key 정보를 application.yml에 저장할 필요가 있다. 이 때 유의할 점은 github와 같은 공개 repository에 이를 공개해서는 안된다.

그리고 application.yml에 등록을 해뒀다면 이를 java 에서 객체형태로 Property 정보를 가져오는 방법을 사용해보도록 하자.

@ConfigurationProperties(prefix = "aws.s3")
@ConstructorBinding
@AllArgsConstructor
@Getter
public class S3Properties {
	private final String bucket;
	private final String accessKey;
	private final String secretKey;
	private final String region;
}

이렇게 등록하고 이를 성공적으로 scan하기 위해 다음과 같이 코드를 추가한다

@SpringBootApplication
@ConfigurationPropertiesScan(basePackages = {"com.almondia.*"}) // 추가할 코드
public class MecaApplication {

	public static void main(String[] args) {
		SpringApplication.run(MecaApplication.class, args);
	}
}

application.yml에서 이제 성공적으로 accessKey와 secretKey를 노출 없이 간편하게 가져올 수 있다. 그 다음엔 이제 AmazonS3Client 타입의 bean을 생성할 수 있도록 Configuration을 작성해보자

@Configuration
@RequiredArgsConstructor
public class S3Configuration {

	private final S3Properties s3Properties;

	@Bean
	public AmazonS3Client amazonClient() {
		return (AmazonS3Client)AmazonS3ClientBuilder.standard()
			.withRegion(s3Properties.getRegion())
			.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(
				s3Properties.getAccessKey(),
				s3Properties.getSecretKey())))
			.build();
	}
}

이제 S3 presignedURL을 생성하기 위한 기초 작업이 끝났다.

Presigned URL 생성 API 만들기

@Component
@RequiredArgsConstructor
public class S3PreSignedUrlRequest {

	private final AmazonS3Client s3Client;
	private final S3Properties s3Properties;

	public URL requestPutPreSignedUrl(String objectKey, Date expiration) {
		GeneratePresignedUrlRequest generatePresignedUrlRequest =
			new GeneratePresignedUrlRequest(s3Properties.getBucket(), objectKey)
				.withMethod(HttpMethod.PUT)
				.withExpiration(expiration);
		return s3Client.generatePresignedUrl(generatePresignedUrlRequest);
	}
}

s3의 presignedurl은 bucket에 등록 및 수정 모두 PUT으로 요청하기 때문에 PUT형태의 presignedUrl을 생성해야한다.
PresignedUrl을 생성할 때 버킷 네임과 objectKey를 생성하는데 objectKey는 bucket에 저장될 경로를 의미한다. 그리고 expiration을 통해 유효기간을 설정한다. 유효기간이 지나면 더 이상 발급받은 presigned url을 사용할 수 없다.

그리고 이를 이용해 Controller 구현에 API를 설계하면 된다. 내가 진행한 프로젝트의 경우, 회원 인증이 된 경우에 presigned url을 요청하면 발급하는 형식으로 설계했다.

profile
엘 프사이 콩그루

0개의 댓글