테이블들이 서로 관계를 맺어 복잡한 데이터 구조를 단순화 하고, 비교적 쉽게 관리 할 수 있게 된다.
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", });
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"); } };
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"); } };
사실 이 다음 수업에서 진행한 내용인데, 그 날 수업 내용이 쿠키, 세션 (+ 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"];