[SeSAC X 코딩온] 웹개발자 풀스택 과정 9주차 회고 (1) | sequelize 심화 (relation), .env 활용(.json -> .js)

옹잉·2024년 2월 21일
0

💡 2/19 sequelize 심화 (relation)


📍 데이터베이스 응용 - 관계 차수와 JOIN

테이블들이 서로 관계를 맺어 복잡한 데이터 구조를 단순화 하고, 비교적 쉽게 관리 할 수 있게 된다.
sequelize로 관계성 설정과 JOIN을 구현할 수 있는데

hasOne, hasMany, belongsTo, belongsMany

관계 차수에 대해 알아보고, 어떻게 테이블 관계성을 설정하는지도 함께 알아볼 것이다.

- 하나의 레코드가 다른 하나의 레코드와 관련되어 있는 관계
- 각 레코드는 서로 한 가지 정보만을 공유
- ex) 사용자와 프로필, 직원과 연차, 주문과 송장
/* 
  1:1 관계 설정, Player:Profile = 1:1
  - Player의 pk가 Profile의 fk가 된다.
  - Profile은 Player에 속해있다. (belongsTo)
 */

PlayerModel.hasOne(ProfileModel, {
  // 두 모델을 연결하는 외래키 설정
  onUpdate: "CASCADE",
  onDelete: "CASCADE",
  foreignKey: "player_id",
});

ProfileModel.belongsTo(PlayerModel, {
  onUpdate: "CASCADE",
  onDelete: "CASCADE",
  foreignKey: "player_id",
});

- 한 쪽 레코드가 다른 쪽 레코드 여러개와 관련되어 있는 관계
- 보통 "부모 - 자식" 엔티티 관계
- ex. 부서와 직원, 고객과 주문, 학교와 학생
/* 
  Team:Player = 1:N
  한 팀에 여러 플레이어가 "속해있음"
*/

TeamModel.hasMany(PlayerModel, {
  sourceKey: "team_id", // 키 이름을 원본과 다르게 사용할 경우 명시적으로 어떤 키를 사용하는지 나타냄
  foreignKey: "teamid", // 이름 변경 가능함, 자동으로 테이블의 pk와 연결해줌
});

PlayerModel.belongsTo(TeamModel, {
  targetKey: "team_id",
  foreignKey: "teamid",
});

- 한 쪽 레코드가 다른 쪽 레코드 여러 개와 관련되고, 반대쪽 레코드도 다른쪽 여러 개의 레코드와 관련되어 있는 관계
- 중간 테이블을 사용하여 구현
- ex) 학생과 과목, 주문과 제품, 배우와 영화
/*
  Game:Team = N:N
  하나의 팀은 여러 경기를 할 수 있고,
  하나의 경기에는 여러 팀(2팀)이 참여
 */

TeamModel.belongsToMany(GameModel, {
  through: TeamGameModel,
  foreignKey: "team_id",
});

GameModel.belongsToMany(TeamModel, {
  through: TeamGameModel,
  foreignKey: "game_id",
});

✅ JOIN

models에서 정의한 관계를 기반으로, sequelize의 include: [{}] 이용해 controller에서 JOIN을 해줄 수 있다.

controller > Cmain.js

// GET /players/:playerId (선수 한명 조회 - Player, Profile)
exports.getPlayer = async (req, res) => {
  try {
    console.log(req.params); // {playerId: 1}
    const { playerId } = req.params;
    const player = await Player.findOne({
      where: {
        player_id: playerId,
      },
      include: [{ model: Profile, attributes: ["position", "salary"] }],
      // include: 두 테이블의 공통 부분 보여줌(inner join)
    });
    res.json(player);
  } catch (error) {
    console.log("getPlayer err : ", error);
    res.status(500).send("server error");
  }
};
// GET /teams/:teamId/players
exports.getTeamPlayers = async (req, res) => {
  try {
    // 1.
    // 팀 정보는 안봐도 됨
    // 특정 팀의 선수 정보만 확인하기 위해서는 Player 모델에서
    // team_id 기준으로 조회
    const { teamId } = req.params;
    // const teamPlayers = await Player.findAll({
    //   where: {
    //     team_id: teamId,
    //   },
    // });

    // 2.
    // 특정 팀의 정보와 해당 팀의 선수 정보까지 확인
    const teamPlayers = await Team.findOne({
      where: { team_id: teamId },
      include: [{ model: Player }],
    }); // join : Team에 Player 정보가 추가된 것 처럼 나온다.
    res.send(teamPlayers);
  } catch (error) {
    console.log("getTeamPlayers err : ", error);
    res.status(500).send("server error");
  }
};

➕ 추가. GET 요청 시 쿼리 스트링에 담긴 정보로 데이터 조회

api.http 파일

@server=http://localhost:8080

### 전체 팀 조회, sort, search
### 이름 순으로 전체 팀 "조회" (query)
GET {{server}}/teams?sort=name_asc

controller > Cmain.js

exports.getTeams = async (req, res) => {
  try {
    console.log("getTeams req.body: ", req.query);
    const { sort, search } = req.query;
    let teams;
    // sort = name_asc >>> 전체 조회 및 이름순 팀 정렬
    // search=[팀이름] >>> 특정 팀 이름만 조회
    if (sort === "name_asc") {
      // 이름 오름차순 정렬
      teams = await Team.findAll({
        attributes: ["team_id", "name"],
        order: [["name", "ASC"]], // ORDER BY name ASC
      });
    } else if (search) {
      teams = await Team.findAll({
        attributes: ["team_id", "name"],
        where: {
          // WHERE name Like '%KT%'
          name: { [Op.like]: `%${search}%` },
        },
      });
    } else {
      // sort 에 name_asc외의 문자열이 들어오거나
      // sort, search가 전달되지 않았을 때
      teams = await Team.findAll({
        attributes: ["team_id", "name"],
      });
    }
    // teams = [] or [{}, {}]
    if (teams.length === 0) res.send("다시 검색하세요.");
    else res.send(teams);
  } catch (error) {
    console.log("getTeams err : ", error);
    res.status(500).send("server error");
  }
};



📍 .env 파일 활용

사실 이 다음 수업에서 진행한 내용인데, 그 날 수업 내용이 쿠키, 세션 (+ JWT 조금)이라 너무 양이 많아 이번에 넣었다.

바로 직전 글에서 환경 변수에 대해 작성했었는데, 이번 실습에서 활용해보았다.

.env 파일에 DB user이름이나 비밀번호 등 중요한 정보들을 작성하는데
sequelize 모듈 사용시 생성되는 config > config.json 파일에서는 .env를 사용할 수 없다.
그래서 config > config.js 파일을 생성해주면 해결할 수 있다.

// [ .env 파일 ] -> .gitignore에 꼭! 추가

DB_PASSWORD=0000
DB_USERNAME=user
// [config.js 파일]

// .env 모듈 설치
require("dotenv").config(); // process.env. 사용 가능

const development = {
  username: process.env.DB_USERNAME,
  password: process.env.DB_PASSWORD,
  database: "sesac",
  host: "127.0.0.1",
  dialect: "mysql",
};

module.exports = { development };
// module.exports = { development, test, production }; // 추가로 내보낼것 있을 때!
// [ models > index.js 파일]

// .env 사용하기 위해 config.js 설정 (.json -> .js)
const config = require(__dirname + "/../config/config.js")["development"];
profile
틀리더라도 🌸🌈🌷예쁘게 지적해주세요💕❣️

0개의 댓글