본문 바로가기

코딩강의/인스타그램클론(expo-노마드코더)

Photos Module(3) - AWS upload 세팅

🍔 핵심 내용

 

🥑 AWS 로그인 및 S3 버켓 만들기

AWS 회원 가입 후, S3 서비스를 이용해보자.

 

먼저, IAM 에서 사용자를 등록해주어야한다.

 

빨간색 체크
빨간색 체크

 

IAM 최종 화면에, access key와 secret access key가 나오는데, 이건 한번 닫히면 다시 못연다. 그렇기 때문에 반드시 잘 기억해 두어야 한다. (여기에서는 .env파일에 저장 및 로그인 할 예정)

 

IAM 등록 완료 후 S3 버킷 만들기

 

S3 누른 다음에 빨간색 체크
S3 버킷 생성 화면

 

🍔 코드 리뷰

 

🥑 .env

DATABASE_URL=비밀
PORT=4000
SECRET_KEY=비밀
AWS_KEY= aws accesskey 입력
AWS_SECRET= aws secret accesskey 입력

받아온 key를 .env에 넣어주자.

 

🥑 shared.utils.js

import AWS from "aws-sdk";

AWS.config.update({
  credentials: {
    accessKeyId: process.env.AWS_KEY,
    secretAccessKey: process.env.AWS_SECRET,
  },
});

 부여 받은 키를 통해 로그인 해주는 utils를 하나 만들어 주자. (아래 이어서)

 


🍔 핵심 내용

 

🥑 AWS와 내 서버 연결 (shared.utils.js 마무리)

 

🍔 코드 리뷰

 

🥑 shared.utils.js

import AWS from "aws-sdk";

AWS.config.update({
  credentials: {
    accessKeyId: process.env.AWS_KEY,
    secretAccessKey: process.env.AWS_SECRET,
  },
});

export const uploadPhoto = async (file, userId) => {
  const { filename, createReadStream } = await file;
  const readStream = createReadStream();
  const objectName = `${userId}-${Date.now()}-${filename}`;
  const { Location } = await new AWS.S3()
    .upload({
      Bucket: "kimstaclone-upload",
      Key: objectName,
      ACL: "public-read",
      Body: readStream,
    })
    .promise();
  return Location;
};

 AWS.S3 object를 만들어 주고, 여기에는 아래 4개 값이 필요하고 마지막에 promise()로 해주어야 한다.

Bucket: AWS의 버켓이름

key: 파일 이름(중복되지 않는 고유 이름으로 설정)

ACL: 모두 읽음 가능

Body: readStream

 

우리가 필요한 데이터는 최종 url값이다. 해당 객체의Location의 값에 url 주소가 들어가기 때문에 해당 최종 return 값으로 넣어준다.

 

 

🥑 editProfile.resolvers.js

import { createWriteStream } from "fs";
import client from "../../client";
import bcrypt from "bcrypt";
import { protectedResolver } from "../users.utils";
import { uploadPhoto } from "../../shared/shared.utils";

const resolverFn = async (
  _,
  { firstName, lastName, userName, email, password: newPassword, bio, avatar },
  { loggedInUser }
) => {
  let avatarUrl = null;
  if (avatar) {
    avatarUrl = await uploadPhoto(avatar, loggedInUser.id);
    // const { filename, createReadStream } = await avatar;
    // const newFilename = `${loggedInUser.id}-${Date.now()}-${filename}`;
    // const readStream = createReadStream();
    // const writeStream = createWriteStream(
    //   process.cwd() + "/uploads/" + newFilename
    // );
    // readStream.pipe(writeStream);
    // avatarUrl = `http://localhost:4000/static/${newFilename}`;
  }
  let uglyPassword = null;
  if (newPassword) {
    uglyPassword = await bcrypt.hash(newPassword, 10);
  }
  const updatedUser = await client.user.update({
    where: { id: loggedInUser.id },
    data: {
      firstName,
      lastName,
      userName,
      email,
      bio,
      ...(uglyPassword && { password: uglyPassword }),
      ...(avatarUrl && { avatar: avatarUrl }),
    },
  });
  if (updatedUser.id) {
    return {
      ok: true,
    };
  } else {
    return {
      ok: false,
      error: "프로필을 업데이트 할 수 없습니다.",
    };
  }
};

export default {
  Mutation: {
    editProfile: protectedResolver(resolverFn),
  },
};

avatar 부분에 완성된 uploadPhoto를 연결해주자. 그리고 받아온 url 주소값을 avatar 필드에 업데이트 해주게 된다.

 

 

아래와 같이, altair graphql 에서 avatar 사진을 올려주게 되면, DB에 해당 url 값이 올라간 걸 확인 할 수 있다.

altair 사진 업로드 테스트

 

 

DB에 등록 완료

uploadPhoto에서도 해당 기능을 써야 한다. 이름이 곂치니 다음 내용에서는 이름을 바꿔주자!

 

🍔 핵심 내용

 

🥑 AWS 폴더 관리

AWS에 파일을 올리고, 이를 폴더로도 관리 해줄 수 있다. 이를 활용해보자.

 

🥑 shared.utils.js

import AWS from "aws-sdk";

AWS.config.update({
  credentials: {
    accessKeyId: process.env.AWS_KEY,
    secretAccessKey: process.env.AWS_SECRET,
  },
});

export const uploadToS3 = async (file, userId, folderName) => {
  const { filename, createReadStream } = await file;
  const readStream = createReadStream();
  const objectName = `${folderName}/${userId}-${Date.now()}-${filename}`;
  const { Location } = await new AWS.S3()
    .upload({
      Bucket: "kimstaclone-upload",
      Key: objectName,
      ACL: "public-read",
      Body: readStream,
    })
    .promise();

  return Location;
};

 

함수명을 변경해 주었다. 그리고 objectName: 파일 이름 맨 앞에 폴더명/ 을 넣어 주었다. AWS에서는 자동으로 파일 이름 앞에  / 가 들어가면 폴더로 인식해줘서 폴더 관리를 해준다. (AWS 똑똑하네!) 세번째 인자 값으로 넣어주자.

 

🥑 editProfile.resolvers.js

import { createWriteStream } from "fs";
import client from "../../client";
import bcrypt from "bcrypt";
import { protectedResolver } from "../users.utils";
import { uploadToS3 } from "../../shared/shared.utils";

const resolverFn = async (
  _,
  { firstName, lastName, userName, email, password: newPassword, bio, avatar },
  { loggedInUser }
) => {
  let avatarUrl = null;
  if (avatar) {
    avatarUrl = await uploadToS3(avatar, loggedInUser.id, "avatar"); <-- 세번째 인자 추가
...}

...

세번째 인자 추가, 이제 해당 파일은 avatar/ 폴더로 관리 된다.

 

🥑 uploadPhoto.resolvers.js

import client from "../../client";
import { uploadToS3 } from "../../shared/shared.utils";
import { protectedResolver } from "../../users/users.utils";
import { processHashtags } from "../photos.utils";

export default {
  Mutation: {
    uploadPhoto: protectedResolver(
      async (_, { file, caption }, { loggedInUser }) => {
        let hashtagObj = [];
        if (caption) {
          hashtagObj = processHashtags(caption);
        }
        const fileUrl = await uploadToS3(file, loggedInUser.id, "upload"); <--세번째 인자추가
        return client.photo.create({
          data: {
            file: fileUrl,
            caption,
            user: {
              connect: { id: loggedInUser.id },
            },
            ...(hashtagObj.length > 0 && {
              hashtags: { connectOrCreate: hashtagObj },
            }),
          },
        });
      }
    ),
  },
};

uploadPhoto resolver 에도 해당 함수를 넣고, 세번째 인자도 추가해주자. 이렇게 세팅을 하고 파일을 올려보면

아래와 같이 자동으로 해당 폴더로 파일이 들어 가게 된다.

 

 

photo module 끝!

'코딩강의 > 인스타그램클론(expo-노마드코더)' 카테고리의 다른 글

Direct Messages(2)  (0) 2021.04.13
Direct Messages(1)  (0) 2021.04.07
Photos Module(2)  (0) 2021.04.04
Photos Module(1)  (0) 2021.03.31
User module(3)  (0) 2021.03.27