[WnB] 방에 연결된게 너무

이 글은 저자가 어디선가 듣고 이해한 것에 대해 개인적인 언어로 쓴 기고문입니다.

잘못된 내용이 있을 수 있습니다.
그러니 읽으셨다면 많은 이용 부탁드립니다!
!
!

결국 우리는 우리가 가진 역사를 통해서만 타자를 이해할 수 있습니다.
.-

방을 만드는 데 필요한 다른 데이터는 무엇입니까? ERD 다이어그램 고려 객실정보, 주소정보, 편의시설정보, 이미지정보, 카테고리정보필요합니다.
먼저 방을 생성한 다음 위의 다른 5개 테이블과 관계를 생성해야 합니다.
방을 만들 때 관계를 정리하는데 정말 좋습니다.
일대일, 일대다 및 다대다 관계가 모두 포함됩니다.
이전에 게시된 사용자 모델과 세부 사용자 간의 관계는 일대일 관계였습니다.
따라서 이번 포스팅에서는 one-to-many에 대해 다루도록 하겠습니다.
ERD 다이어그램을 보면 공간과 이미지의 관계는 일대다 관계로 성립된다.
즉, 한 방에 여러 이미지가 있을 수 있으며 한 방에 이미지가 있어야 합니다.

1:n 관계의 모델과 관계를 정리해보자

룸 모델

먼저 다른 모델이 방의 ID 값을 참조할 수 있도록 방 개체를 만듭니다.
방 생성에 필요한 최소 데이터는 다음과 같습니다.
다른 모든 데이터는 다른 테이블에 연결됩니다.
방을 만든 사람을 알 수 있는 hostId와 카테고리에 속할 categoryId가 있습니다.
그 외에도 이름, 가격 또는 설명이 있습니다.
또한 지난번에 추가된 isCreatedAll의 경우 모든 옵션이 추가되었을 때 boolean이 true로 변경되었습니다.
그것이 이루어질 때까지 사용자는 방 목록을 볼 때 볼 수 없습니다.

class Room extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        hostId: {
          type: Sequelize.INTEGER,
          allowNull: false,
        },
        categoryId: {
          type: Sequelize.INTEGER,
          allowNull: false,
        },
        roomName: {
          type: Sequelize.STRING,
          allowNull: false,
        },
        price: {
          type: Sequelize.INTEGER,
          allowNull: false,
        },
        description: {
          type: Sequelize.STRING,
          allowNull: false,
        },
        isCreatedAll: {
          type: Sequelize.BOOLEAN,
          allowNull: false,
          defaultValue: false,
        },
      },
    );
  }
}

이미지 모델

이미지가 가지고 있는 필드입니다.
먼저 roomId가 있습니다.
그리고 이미지는 Cloudinary로 업로드하기 위해 아래와 같이 roomImageUrl 필드와 roomImageFileName 필드를 가집니다.
roomImageUrl은 cloudinary에 저장된 이미지의 주소가 되고 roomImageFileName은 cloudinary에 있는 파일을 삭제하기 위해 필요한 이미지의 이름이 됩니다.

class RoomImage extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        roomId: {
          type: Sequelize.INTEGER,
          allowNull: false,
        },
        roomImageUrl: {
          type: Sequelize.STRING,
          allowNull: false,
        },
        roomImageFileName: {
          type: Sequelize.STRING,
          allowNull: false,
        },
      },
    );
  }
}

일대다 관계

일대다 관계는 흔히들 말하지만 하나에 여럿이 있을 수 있고 다수 중 하나에 해당하는 것이 전자에 포함되어야 한다.
일대다 관계에 대해 sequenceize에서 사용할 수 있는 관계 유형은 hasMany() 및 wantsto()입니다.
이전에는 1대 1 관계에서도 사용되었기 때문에 wantsto가 친숙할 수 있습니다.
소스 모델. hasMany(대상 모델) 및 소스 모델. “belongs to”(대상 모델)의 형태로 사용됩니다.

// 방 모델
static associate(db) {
    Room.hasMany(db.RoomImage, { foreignKey: "roomId", sourceKey: "id" });
}

// 방이미지모델
static associate(db) {
    RoomImage.belongsTo(db.Room, { foreignKey: "roomId", targetKey: "id" });
}

두 가지 모두에 사용할 수 있는 한 가지 옵션은 ForeignKey입니다.
ForeignKey가 설정되지 않은 경우 모델 + 기본 키 형태로 외래 키가 생성됩니다.
이 경우 방 이미지 테이블이 생성됩니다.
저의 경우 위의 모델을 보면서 방 이미지 모델에 roomId라는 필드를 직접 추가했습니다.
hasMany에서 sourceKey를 사용하고 속한 targetKey를 사용할 수 있습니다.

많이있다()

소스 모델. 대상 모델에는 hasMany(대상 모델) 형식의 외래 키가 있습니다.
sourceKey는 대상 모델의 외래 키가 참조하는 소스 모델의 값을 의미합니다.
sourceKey는 대상 모델(RoomImage)의 외래 키(roomId)가 참조하는 소스 모델(Room)의 값(id)을 의미합니다.

들었다()

외래 키 소스 model.belongsTo(대상 모델)에는 소스 모델이 포함되어 있습니다.
targetKey는 소스 모델의 외래 키가 참조하는 대상 모델의 값을 의미합니다.
targetKey는 소스 모델(RoomImage)의 외래 키(roomId)가 참조하는 대상 모델(Room)의 값(id)을 의미합니다.

cloudinary에 이미지를 등록하자

Amazon의 s3와 동일한 기능을 제공하는 것 중 하나가 Cloudinary입니다.
이전 포스팅에서 Cloudinary 사용법을 다루었기에 이번 프로젝트에서 방 이미지를 등록하는데 사용했습니다.
Cloudinary를 사용하려면 Cloudinary 인스턴스를 생성해야 합니다.
이는 Cloudinary 관련 키입니다.
Cloudinary 인스턴스를 생성한 경우 다음과 같이 저장 공간을 설정합니다.

//클라우디너리 설정파일
require("dotenv").config();
const env = process.env;

const RoomCloudinary = require("cloudinary").v2;
const { CloudinaryStorage } = require("multer-storage-cloudinary");

//cloudinary의 계정과 코드에서 생성되는 cloudinary의 인스턴스를 연결해준다.
RoomCloudinary.config({ cloud_name: env.CLOUDINARY_CLOUD_NAME, api_key: env.CLOUDINARY_KEY, api_secret: env.CLOUDINARY_SECRET, }); // 저장공간에 대한 설정이다.
const roomStorage = new CloudinaryStorage({ cloudinary: RoomCloudinary, params: { folder: "Rooms", allowedFormats: ("jpeg", "png", "jpg"), }, }); module.exports = { RoomCloudinary, roomStorage, };

위의 코드를 완성하면 Cloudinary를 사용하기 위한 기본 구성이 완료된 것입니다.
다음으로 실제로 이미지 데이터를 수신할 때 구문 분석을 활성화해야 합니다.

이미지 분석이 필요할 때

메모리 설정을 가져오고 multer로 구문 분석합니다.
우리가 일반적으로 사용하는 content_type은 json입니다.
그러나 이미지를 저장하려면 Content_Type이 multipart/form-data여야 하므로 Multer로 파싱할 수 있습니다.
아래와 같이 구성하면 uploadRoom에 배열과 단일 메서드가 생성됩니다.
array는 여러 이미지를 추가하기 위한 것이고 single은 단일 이미지를 추가하기 위한 것입니다.

//라우터
const { roomStorage } = require("../cloudinary/rooms");
const multer = require("multer");
const uploadRoom = multer({ storage: roomStorage });

// 등록된 방에 이미지 추가하기
router.post(
  "/:host_id/rooms/:room_id/images",
  isLoggedIn, //로그인이 되었는지
  isHost, //호스트인지 아닌지
  authorHost,// 호스트라면 권한이 있는지
  authorRoom,// 방에 대한 권한이 있는지
  uploadRoom.array("rooms"),//실제로 이미지를 변환
  wrapAsync(hosts.addRoomImage)
);

컨트롤러로 이동

//컨트롤러
module.exports.addRoomImage = async (req, res) => {
  // 이미지파일을 돌면서 map으로 새로운 이미지변수를 만든다.
const images = req.files.map((f) => ({ roomImageUrl: f.path, roomImageFileName: f.filename, })); for (let image of images) { await RoomImage.create({ roomId: req.currentRoom.id, // 앞서 라우터의 미들웨어에서 추가해준 것이다.
roomImageUrl: image.roomImageUrl, roomImageFileName: image.roomImageFileName, }); } req.currentRoom.isCreatedAll = true; await req.currentRoom.save(); return res.status(200).json({ message: "이미지가 추가 되었습니다.
" }); };

이미지가 컨트롤러에 전달되어 배열로 사용되면 req.files(Single의 경우 파일)에 포함됩니다.
RoomImage 객체가 생성되고 데이터베이스가 클라우드에 저장됩니다.
저장된 이미지 URL과 이미지 이름이 저장됩니다.

오늘의 리뷰

방을 만들기 위해 더 많은 오브젝트 데이터가 필요해서 한번에 다 구현하지 않고 따로따로 구현했는데 맞는지 모르겠습니다.
이 글을 쓰다가 오류를 발견해서 수정했습니다.
현재 제가 구현하고 있는 코드는 방, 주소, 시설, 이미지 등을 생성합니다.
우선 space 필드에 isCreatedAll이라는 필드를 넣어 모든 데이터가 있는지 없는지 판단하는 것이 좋다고 생각하지만, 만약 그렇다면 함수별로 필드를 만들어야 한다고 생각합니다.
주소를 만들고, 주소가 등록된 필드를 true로 변경하고, 편의를 만들고, 편의가 등록된 필드를 true로 변경합니다.
프로젝트가 진행될수록 고쳐야 할 부분도, 줘야 할 부분도 많아지는 것 같다.
하지만,…….우선 고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고고