ORM과 SQL

suji·2022년 8월 6일

데이터베이스의 기본 용어를 정리하면서,
'cluvie'프로젝트에서 왜 관계형 데이터베이스를 설계하였고,
왜 ORM과 SQL을 섞어 사용했는지 풀어보려고 합니다.

✔️ 용어 정리

DBMS

  • DataBase Management System
  • 사용자와 데이터베이스 사이에서 사용자의 요구에 따라 정보를 생성
  • 데이터베이스를 관리해주는 소프트웨어

RDBMS

  • Strucured Query Language
  • 관계형 데이터베이스 관리 시스템의 데이터를 관리하기 위해 설계된 특수 목적의 프로그래밍 언어
  • 자료의 검색과 관리, 데이터베이스 스키마 생성과 수정, 데이터베이스 객체 접근 조정관리를 위해 고안됨

관계형 데이터베이스(RDBMS)란 행과 열을 가지는 표 형식으로 데이터를 2차원 테이블 형태로 저장하는 방식으로, SQL이라는 언어를 써서 조작한다

RDBMS 특징

  • 명확한 데이터 구조 보장
  • 테이블간의 관계를 나타내기 위해 외래키(foreign key) 사용
  • 외래키를 이용한 테이블 간 join 가능
  • 스키마가 변경 될 경우 번거롭고 어려움

NoSQL

  • Not Only SQL
  • 관계를 정의하지 않는 그냥 하나의 테이블
  • 빅데이터의 등장으로 RDBMS의 단점인 성능을 향상시키기 위해 등장
  • 비용을 고려하여 데이터 일관성은 포기하되, 여러 대의 데이터에 분산하여 저장

NoSQL 특징

  • 스키마가 없기 때문에 유연하며 자유로운 데이터 구조
  • 언제든 저장된 데이터 조정하고 새로운 필드 추가 가능
  • 데이터 분산 용이
  • 데이터 중복이 발생할 수 있으며, 데이터가 변경될 경우 모든 컬렉션에서 수정 수행
  • 스키마가 존재하지 않기에 데이터 구조 결정 어려움

✔️ 왜 MySQL을 선택했을까?

"cluvie" 프로젝트: 모임 주선 프로젝트

모임을 만들면, 유저는 모임가입 및 모임 찜하기 등 행위를 할 수 있다
가입하기, 찜하기의 기능이 모임과 연관되어있으며,
여러명의 유저가 모임하나에 가입하거나 찜할 수 있는 등
데이터간의 참조가 많고 테이블을 join하여 사용할 일이 많다고 생각되어

이에 적합한 관계형데이터베이스 MySQL을 선택했다!

또한,
SQL 작업을 쉽게, javascript코드로 SQL 제어가능하도록
객체를 통해 간접적으로 데이터베이스를 다루기 위해
ORM 라이브러리인 Sequelize를 사용하였다

그러나..

프로젝트 특성상 기간이 짧고
기획까지 팀원들과 해야하기 때문에 데이터의 스키마가 고정되어있지 않고 변동될 가능성이 많고
외래키를 사용하게 되면 확장가능이 매우 어려워지는
문제점에 부딪혔다

그래서 외래키는 설정하지 않았다!

그 대신, 연관되어있는 데이터를 조회할때 query문을 사용했다

테이블의 관계를 한눈에 볼 수 있도록 잘 정리하면서 스키마를 작성했다
비록 중간중간 수정이 많긴했지만,
이렇게 정리한것을 팀 노션페이지에 공유하였고,
수정될때마다 팀모두에게 공지하였다

덕분에 프론트 팀원들과도 소통이 잘 되었다

sequelize를 사용하면서, query문도 사용하는 예시를 보여주자면

static getClubDetail = async ({ club_id }) => {
    const club = await Clubs.findOne({ where: { id: club_id } });
    let sql = `SELECT description FROM clubs WHERE id = ${club_id};`;
    const clubDetail = await db.sequelize.query(sql, {
      type: db.sequelize.QueryTypes.SELECT,
    });
    return clubDetail;
  };

위와같은 문법을 사용하여 query문을 추가할 수 있었다
매번 조회해야하기 때문에 속도 측면에서는 단점일 수 있지만,
편리한 orm을 사용하면서도 자유롭게 데이터를 조회할 수 있다는 점
팀프로젝트에서 큰 장점으로 느껴졌다

짜는데에는 정말 복잡했지만 짜고나니 query의 장점을 제대로 알게된 코드가 있다

static getClubListTest = async ({ user_id, club_id }) => {
    let sql = `SELECT l.user_id, c.id, c.name, c.manager, c.picture, c.intro, c.duration, c.state, c.online, c.offline, c.description, c.views, c.head_count, c.weekday, c.weekend, c.hashtag1, c.hashtag2, c.created_at, c.updated_at FROM clubs AS c LEFT JOIN (SELECT * FROM likes WHERE user_id=${user_id}) AS l ON c.id = l.club_id WHERE c.id < ${club_id} ORDER BY id DESC LIMIT 6`;
    const clubs = await db.sequelize.query(sql, {
      type: db.sequelize.QueryTypes.SELECT,
    });
    // 클럽목록 배열로 보낼 때, 로그인한 유저의 모임 찜 여부를 user_id: 0 or 1 로 표현
    const clubListView = clubs.map((club) => {
      if (club.user_id != null) {
        return { ...club, user_id: 1 };
      } else {
        return { ...club, user_id: 0 };
      }
    });
    return clubListView;
  };

딱 보아도 클린코드는 아니지만,,,

query문을 설명하자면

  • club 테이블의 데이터를 불러온다
  • like 테이블에서 좋아요를 누른 user의 아이디(user_id)도 같이 불러온다
  • 단 club 테이블에 like 테이블을 left join할때, 로그인되어있는 user_id와 좋아요 누른 user의 아이디(l.user_id)가 같은 경우에만 like테이블을 club 테이블에 join 한다
  • join 할때에는 club.id 와 like.club_id가 일치하는 기준으로 join 한다
  • 추가적으로 무한스크롤을 구현하기 위해 마지막 조회한 club.id 를 기준으로 6개씩 데이터를 불러온다

또한,
결과적으로 user가 좋아요 누른 club에는 user_id가 join 되어 있는데
프론트에서 데이터를 받았을 때 보기 편하도록
좋아요 누른 club의 경우 user_id를 '1', 누르지 않은 경우 '0'으로 반환하여
return 시켰다

profile
문제를 해결하는 백엔드 개발자

0개의 댓글