[Spring, AWS] S3 객체별로 접근권한 설정하기

김재연·2023년 8월 21일
0

수숙관

목록 보기
17/17
post-thumbnail

프로필 사진 API를 추가 구현하면서 이미지 접근권한을 다시 설정해야할 필요가 생겼다.

기존의 숙제 인증피드는 권한이 있는 사람만 볼 수 있어야 했기 때문에 버킷의 접근권한은 건드리지 않고(퍼블릭 설정X) presigned URL을 발급해서 객체에 접근했는데, 프로필사진은 누구나 볼 수 있어야 하기 때문에 public 설정으로 하는게 맞다고 생각했다. presigned URL은 최대기한이 7일인가 그런데 프사를 7일 주기로 받아오는 것보단 어느때나, 계속 볼 수 있는게 상식적으로 맞다고 생각했기 때문이다.

그런데 그렇다고 같은 버킷 안에 있는, 기존에 있던 숙제 인증피드들까지 public으로 바꿀 순 없는지라 폴더별로 접근권한을 다르게 주는 방법을 찾아보았다.

🗂️ 내가 원하는 S3 버킷 내부 객체들의 파일구조와 접근권한

bucket => private2번 유저의 숙제 인증피드 폴더 => private
    	ㄴ 파일1 (private)
        ㄴ 파일2 (private)...12번 유저의 숙제 인증피드 폴더 => private
		ㄴ 파일1 (private)
        ㄴ 파일2 (private)...
	ㄴ 프로필 사진 폴더 => public2번 유저의 프로필 사진 (public)7번 유저의 프로필 사진 (public)12번 유저의 프로필 사진 (public)...

시행착오

1. S3의 ACL 퍼블릭 설정

프로필사진은 profile/ 폴더에 모아둘거라 S3 버킷 안에 profile/ 폴더를 만들고,

profile/ 폴더를 선택해서 작업 > ACL을 사용하여 퍼블릭으로 설정을 눌러준다.

그러면 다른 객체들은 여전히 presigned URL로 접근해야하지만 profile/ 안에 있는 객체들은 public으로 설정되어 객체 url로 바로 접근할 수 있다.


2. 스프링에서 객체 url 가져오기

public class S3Service {
    @Value("${cloud.aws.s3.bucket}")
    private String bucket;
    private final AmazonS3 amazonS3;
    
    public String getPublicURL (String keyName) {
        return amazonS3.getUrl(bucket, keyName).toString();
    }
}
...
if (user.getProfileImg() != null) {
	String url = s3Service.getPublicURL(user.getProfileImg());
	return ResponseEntity.ok(url);
}
...

3. 결과


BUT,,,,

이렇게 아주 쉽고 순조롭게 끝나는가 싶었으나..

프로필사진이 업로드는 되는데 가져올때 access denied가 뜬다는 팀원분의 카톡이 와버렸다.

어라?? 뒤적뒤적...

알고보니 AWS에 직접 접속해서 한 폴더에 ACL을 사용하여 퍼블릭으로 설정을 해주면, 그 순간에 해당 폴더 안에 있는 객체들도 다 퍼블릭 설정이 먹힌다.

그런데 그 이후에 해당 폴더에 추가된 객체에는 폴더의 퍼블릭 설정이 먹히지 않는 것이었다.

그렇다고 새로운 파일이 추가될 때마다 직접 들어가서 퍼블릭 설정을 눌러줄 수는 없는 노릇...

어떻게하지?? 고민하다가 아예 업로드할때 public으로 권한 설정을 시켜서 올릴 수는 없나?라는 생각이 나서 찾아봤더니 객체 업로드를 할때 이런 ACL 권한을 포함시켜서 업로드하는 방법이 있었다.


객체 업로드할 때 PublicRead 권한도 같이 업로드하기

다른 부분은 그냥 업로드할 때랑 똑같다.

public String uploadPublic(MultipartFile multipartFile, String s3FileName) throws IOException {
	ObjectMetadata objMeta = new ObjectMetadata();
    objMeta.setContentLength(multipartFile.getInputStream().available());
	amazonS3.putObject(
		new PutObjectRequest(bucket, s3FileName, multipartFile.getInputStream(), objMeta)
                        .withCannedAcl(CannedAccessControlList.PublicRead)
	);
	return URLDecoder.decode(amazonS3.getUrl(bucket, s3FileName).toString(), "utf-8"); // url에 한글&특수문자가 포함되어있을 경우 깨짐 방지
}

바뀐 부분은 이 부분이다.

// 기존 업로드 코드
amazonS3.putObject(bucket, s3FileName, multipartFile.getInputStream(), objMeta);

// 객체와 ACL을 함께 업로드하는 코드
amazonS3.putObject(
	new PutObjectRequest(bucket, s3FileName, multipartFile.getInputStream(), objMeta)
	.withCannedAcl(CannedAccessControlList.PublicRead)
);

withCannedAcl()를 활용해서 업로드할 파일에 ACL 권한설정을 같이 보내주는 것이다.

CannedAccessControlListPublicRead말고도 더 다양한 권한도 있으니 여기를 참고해서 적절히 쓰면 된다.

객체 url을 가져오는 부분은 위에서 쓴 것과 동일하다. (삭제도 마찬가지)


(1) ACL 권한 설정 없이 업로드했을 때

(2) ACL 권한 설정을 PublicRead로 하고 업로드했을 때

이렇게 객체의 권한을 봤을 때, 퍼블릭 액세스에 읽기가 가능하다고 뜨면 이제 객체 url을 통해 아무때나, 누구나 이 파일에 접근할 수 있게 된다.



+) 캐시삭제

이 문제는 아니고 다른건데 그냥 프로필사진 포스팅을 쓰는 김에 쓴다.

어차피 프로필사진은 한 유저당 한개이기 때문에 여러개를 가지고 있을 필요가 없어서, 프로필사진 변경을 하면 기존 프사를 지우고 똑같은 이름으로 새로운 파일을 올리는 방식으로 코드를 짰었다. (덮어쓰기같은 느낌) 그런데 프사 변경을 했는데 객체 url로 가져올때 이전 파일이 뜬다는 오류가 발견됐다.

이상하게 파일 변경을 하고, AWS에 들어가서 열기로 파일을 열면 파일은 분명 바뀌어있는데, 객체 url로 파일에 접근을 하면 업데이트가 안되고 이전파일이 불러와졌다.

결론은 그냥 캐시 문제였다.

프론트단의 리액트 네이티브에서 캐시 리로드?를 해서 해결했다고 한다. 👏👏

profile
일기장같은 공부기록📝

0개의 댓글