Today I learned
비슷한 사진을 추천해주는 기능을 작업하고 있다
구글 비전을 이용해서 사진에 라벨링을 하고 그 라벨을 이용해서 사진을 추천해주는 기능이다
구글 비전 API를 이용해서 생각 보다 쉽게 사진을 분석하고 라벨링을 뽑아올 수 있었다
async createPhotospot(
createPhtospotDto: CreatePhotospotDto,
files: Express.Multer.File[],
userId: number,
collectionId: number
): Promise<void> {
const collection = await this.collectionRepository.findOne({ where: { id: collectionId } });
if (_.isNil(collection)) {
throw new NotFoundException('해당 콜렉션을 찾을 수 없습니다.');
}
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const { title, description, latitude, longitude }: CreatePhotospotDto = createPhtospotDto;
const photospot = await queryRunner.manager
.getRepository(Photospot)
.insert({ title, description, latitude, longitude, userId, collectionId });
for (const file of files) {
try {
const image = await this.s3Service.putObject(file);
const photo = await queryRunner.manager
.getRepository(Photo)
.insert({ image, userId, photospotId: photospot.identifiers[0].id });
const photoKeywords = await this.googleVisionService.image(image);
for (const photoKeyword of photoKeywords) {
if (_.isUndefined(photoKeyword)) {
continue;
}
const photo = await this.photoRepository.findOne({where: {id: photoId}});
const preKeyword = await this.photoKeywordRepository.findOne({ where: { keyword: photoKeyword } });
if (_.isNil(preKeyword)) {
const insertKeyword = await this.photoKeywordRepository.save({ keyword: photoKeyword });
await this.dataSource.query(`INSERT INTO photo_photo_keywords_photo_keyword VALUES (${photo.id}, ${insertKeyword.id})`);
} else {
await this.dataSource.query(`INSERT INTO photo_photo_keywords_photo_keyword VALUES (${photo.id}, ${preKeyword.id})`);
}
}
} catch (error) {
console.log(error);
throw new Error('Photo 입력 실패.');
}
}
await queryRunner.commitTransaction();
} catch (error) {
console.log(error);
await queryRunner.rollbackTransaction();
throw new BadRequestException('요청이 올바르지 않습니다.');
} finally {
await queryRunner.release();
}
}
해당 코드 처럼 구현을 했다 하지만 여기서 문제가 발생했다
사진 업로드의 시간이 너무 오래걸린다는 것이다 로컬에서 혼자 테스트를 하는 경우에도 시간이 오래 걸리는데 여러 사용자가 사용한다고 한다면 생각만해도 사용할 수 없는 기능이라고 생각한다
이 문제를 해결해야한다
문제는 구글 비전을 이용해서 라벨을 뽑아오는 시간이 너무 오래 걸리는 부분이다
생각을 해봤다 과연 await를 걸어서 기다릴 만큼 포토스팟과 포토를 저장할 때 중요한가?
나는 아니라고 생각했다 포토와 포토스팟은 같이 저장되고 사용자에게 보여줘야 하지만 라벨의 경우 나중에 붙이고 사진 추천기능이 필요할 때 사용할 수 있기만 하면 되는 부분이다
그래서 사진 라벨을 뽑아오고 저장하는 부분은 동기적으로 동작할 필요가 없고 비동기적으로 작동시크는 방법으로 코드를 수정했다
async createImageKeyword(image: string, photoId: number): Promise<void> {
const photoKeywords = await this.googleVisionService.image(image);
const keyword = []
for (const photoKeyword of photoKeywords) {
if (_.isUndefined(photoKeyword)) {
continue;
}
const photo = await this.photoRepository.findOne({where: {id: photoId}});
const preKeyword = await this.photoKeywordRepository.findOne({ where: { keyword: photoKeyword } });
if (_.isNil(preKeyword)) {
const insertKeyword = await this.photoKeywordRepository.save({ keyword: photoKeyword });
await this.dataSource.query(`INSERT INTO photo_photo_keywords_photo_keyword VALUES (${photo.id}, ${insertKeyword.id})`);
} else {
await this.dataSource.query(`INSERT INTO photo_photo_keywords_photo_keyword VALUES (${photo.id}, ${preKeyword.id})`);
}
}
}
}
키워드만 처리해주는 메서드를 만들고 비동기로 작동시키는 방식으로 수정했다
이 코드에서 사용자가 사용하면서 포토스팟이 저장되는게 너무 느리다라고 느끼는 성능적인 부분을 개선하면서 문제를 해결했다
이제는 두 번째 문제다 키워드 만큼 select, insert를 너무 자주한다는 것이다 insert만큼은 줄일 수 있지 않을까?
문제를 해결해보도록 하자
async createImageKeyword(image: string, photoId: number): Promise<void> {
const photoKeywords = await this.googleVisionService.image(image);
const keyword = []
for (const photoKeyword of photoKeywords) {
if (_.isUndefined(photoKeyword)) {
continue;
}
const preKeyword = await this.photoKeywordRepository.findOne({ where: { keyword: photoKeyword } });
if (_.isNil(preKeyword)) {
const insertKeyword = await this.photoKeywordRepository.save({ keyword: photoKeyword });
keyword.push(insertKeyword);
} else {
keyword.push(preKeyword);
}
}
const photo = await this.photoRepository.findOne({where: {id: photoId}});
if (_.isNil(photo)) {
return;
}
photo.photoKeywords = keyword;
await this.photoRepository.save(photo);
}
키워드 객체를 배열에다 저장을 하고 save를 이용해서 insert를 한 번 진행하는 방식으로 코드를 수정했다
typeorm의 옵션을 logging을 확인 할 수 있게 하고 수정 전 코드와 수정 후 코드를 비교해보면 확실하게 쿼리의 양이 줄어든 것을 확인할 수 있었다
'과거공부모음' 카테고리의 다른 글
Nest.js에서 Multer를 이용해 여러 장의 이미지 파일 관리 (0) | 2023.04.01 |
---|---|
20230324 TIL - 사진 리사이징 (0) | 2023.03.25 |
20230309 TIL - 여러장의 이미지 업로드 (0) | 2023.03.09 |
20230308 TIL - seed data만들기 (0) | 2023.03.09 |
20230307 TIL - github에 민감한 파일 올렸다 (0) | 2023.03.07 |