방명록 서비스를 만들던 중,
이미지 생성 외의 작업
을 하기 위해 s3의 key값을 따로 관리할 필요가 생겼다.
주석과 함께 기존 코드를 확인해보자.
@Injectable()
export class S3Service {
...
async uploadFile(
file: Express.Multer.File | undefined,
): Promise<string | undefined> {
...
await this.s3.send(uploadCommand);
// 해당 모듈에서는 파일을 업로드하면, url이 반환된다.
// 현재 uploadFile 메서드의 영향을 받는 테이블은 두 개이다.
// GuestBook 테이블과 UserProfile 테이블.
return `https://${this.bucketName}.s3.${awsRegion}.amazonaws.com/${key}`;
}
...
}
model GuestBook {
...
imageUrl String? @unique
...
}
model UserProfile {
...
avatarImageUrl String? @unique
homeImageUrl String? @unique
...
}
현재 테이블 상태처럼 URL로 이미지를 관리하면
위의 필요성 두 가지(이미지 생성 외의 작업 처리
, 클라이언트 측에서의 빠른 이미지 로드
)를 달성할 수 없다.
달성하려고 해도, 서비스 로직에서 매번 Url에서 뒤에 붙어있는 키 값만 따로 떼어내는 작업이 필요하기 때문에 URL이 아닌 key 값을 따로 테이블로 빼서 관리할 필요
가 있다.
이렇게 하면, 이미지를 사용하는 곳(클라이언트 측, S3서비스 모듈)에서는 URL만 수정하면(클라이언트 측에서 ${BaseURL}/w140/${key}
이런 식으로 사용한다든지..) 목표를 달성할 수 있다.
...
model Image {
id String @id
imageUrl String
guestBook GuestBook? @relation("GuestBookImage")
avatarProfile UserProfile? @relation("AvatarImage")
homeProfile UserProfile? @relation("HomeImage")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deleted Boolean @default(false)
}
model UserProfile {
...
avatarImageKey String? @unique
avatarImage Image? @relation("AvatarImage", fields: [avatarImageKey], references: [id])
homeImageKey String? @unique
homeImage Image? @relation("HomeImage", fields: [homeImageKey], references: [id])
...
}
model GuestBook {
...
imageKey String? @unique
image Image? @relation("GuestBookImage", fields: [imageKey], references: [id])
...
}
방법 1은 이미지 테이블을 따로 관리하는 방안이다.
GuestBook과 Image를 1:1, UserProfile과 Image를 1:1 관계로 처리하였으며, UserProfile에 들어가는 이미지는 homeImage와 avatarImage이므로 관계를 명확히 구분하기 위해 relation 어노테이션에 name 값을 주었다.
더하여, 이미지 테이블 그 자체로 기능하는 경우보다 GuestBook과 UserProfile 테이블에서 이미지 테이블을 조인해서 사용하는 경우가 더 많을 것이므로 연관관계의 주인(외래키를 갖는 테이블)을 GuestBook과 UserProfile로 설정하였다.
이렇게 했을 때 생각해 본 몇 가지 장점과 단점은 아래와 같다.
model GuestBook {
...
imageKey String? @unique
...
}
model UserProfile {
...
avatarImageKey String? @unique
homeImageKey String? @unique
...
}
방법 2는 단순히 이미지를 사용하는 테이블의 필드명을 ~Url
에서 ~Key
로 바꾸는 방법이다.
방법 1과 2를 놓고 어떤 것을 선택할지 결정하는 일은 기준이 명확했기 때문에 생각보다 쉬웠다.
확장성과 중앙화된 관리를 원한다면, 방법 1을 선택하면 된다.
반대로 추후 이미지 관련 필드가 추가될 가능성이 적고, 더 단순한 테이블 구조를 선호한다면 방법 2를 선택하면 된다.
현재 프로젝트의 규모가 앞으로 더 확장될 여지가 크지 않고, 이미지 관련 메타 데이터는 S3 Client를 사용해 가져올 수 있으므로 나는 방법 2를 사용해 문제를 해결하였다.