오늘의 목표
✔️ 프로그래머스 코테 문제 풀이
✔️ 팀프로젝트 진행
⏱️ 오늘의 일정
프로그래머스 코테 문제 풀이팀프로젝트 진행
📜 프로그래머스 코테 문제 풀이
최댓값과 최솟값
문제 설명대로 쉽게 풀 수 있었다.
function solution(s) {
var answer = [];
let splitArray = s.split(" ").map(x => +x);
let max = -999999;
let min = 999999;
for (let i = 0; i < splitArray.length; i++) {
if (splitArray[i] >= max) {
max = splitArray[i];
}
if (splitArray[i] <= min) {
min = splitArray[i];
}
}
answer.push(min);
answer.push(max);
return `${answer[0]} ${answer[1]}`;
}
📜 팀 프로젝트 진행
팀 프로젝트를 진행했다.
내가 맡은 회원가입과 로그인을 구현했다.
회원가입
usersRouter.post('/SignUp', async (req,res,next)=>{
const { email, password, passwordCheck } = req.body;
console.log(email, password);
const isExistUser = await prismaUser.user.findFirst({
where:{
email : email
}
});
if(email.length === 0)
{
return res.status(404).json({ message: "이메일을 입력해주세요" });
}
if(password.length === 0)
{
return res.status(404).json({message:"비밀번호를 입력해주세요"});
}
if(isExistUser)
{
return res.status(404).json({message:"이미 존재하는 이메일입니다."});
}
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = await prismaUser.user.create({
data:{
password: hashedPassword,
email : email,
highScore : 0
}
});
return res
.status(201)
.json({ status: 201, message: `${email}로 회원가입이 완료되었습니다.` });
})
이메일 형식으로 아이디를 받고,
비밀번호를 입력받아 유저를 생성해 회원가입을 완료시켜준다.
이메일 검증하는 부분이 현재는 없긴한데, 정규표현식을 이용해 이메일 포멧 적용할 예정.
로그인
usersRouter.post('/SignIn', async (req, res, next) => {
const { email, password } = req.body;
if (email.length === 0) {
return res.status(404).json({ message: "이메일을 입력해주세요" });
}
if(password.length ===0){
return res.status(404).json({message:"비밀번호를 입력해주세요"});
}
const LoginReqUser = await prismaUser.user.findFirst({
where: {
email: email
}
});
if(!LoginReqUser)
{
return res.status(404).json({ message:`${email}은 존재하지 않는 이메일입니다.`});
}
if(!(await bcrypt.compare(password, LoginReqUser.password))){
return res.status(404).json({message:`비밀번호가 일치하지 않습니다.`});
}
const s2cAccessToken = CreateAccessToken(email);
res.header("authorization", s2cAccessToken);
return res
.status(201)
.json({ status: 201, message: `${email}로 로그인 성공` });
})
이메일과 비밀번호를 검증하고 성공하면 AccessToken을 발급해 header에 기록한다.
회원가입과 로그인을 구현하고 나서, game.js에 있는 코드를 클래스화 시켰다.
game.js
import { Base } from "./base.js";
import { Monster } from "./monster.js";
import { Tower } from "./tower.js";
/*
어딘가에 엑세스 토큰이 저장이 안되어 있다면 로그인을 유도하는 코드를 여기에 추가해주세요!
*/
let serverSocket; // 서버 웹소켓 객체
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const NUM_OF_MONSTERS = 5; // 몬스터 개수
let userGold = 0; // 유저 골드
let base; // 기지 객체
let baseHp = 0; // 기지 체력
let towerCost = 0; // 타워 구입 비용
let numOfInitialTowers = 0; // 초기 타워 개수
let monsterLevel = 0; // 몬스터 레벨
let monsterSpawnInterval = 0; // 몬스터 생성 주기
const monsters = [];
const towers = [];
let score = 0; // 게임 점수
let highScore = 0; // 기존 최고 점수
let isInitGame = false;
// 이미지 로딩 파트
const backgroundImage = new Image();
backgroundImage.src = "images/bg.webp";
const towerImage = new Image();
towerImage.src = "images/tower.png";
const baseImage = new Image();
baseImage.src = "images/base.png";
const pathImage = new Image();
pathImage.src = "images/path.png";
const monsterImages = [];
for (let i = 1; i <= NUM_OF_MONSTERS; i++) {
const img = new Image();
img.src = `images/monster${i}.png`;
monsterImages.push(img);
}
let monsterPath;
function generateRandomMonsterPath() {
const path = [];
let currentX = 0;
let currentY = Math.floor(Math.random() * 21) + 500; // 500 ~ 520 범위의 y 시작 (캔버스 y축 중간쯤에서 시작할 수 있도록 유도)
path.push({ x: currentX, y: currentY });
while (currentX < canvas.width) {
currentX += Math.floor(Math.random() * 100) + 50; // 50 ~ 150 범위의 x 증가
// x 좌표에 대한 clamp 처리
if (currentX > canvas.width) {
currentX = canvas.width;
}
currentY += Math.floor(Math.random() * 200) - 100; // -100 ~ 100 범위의 y 변경
// y 좌표에 대한 clamp 처리
if (currentY < 0) {
currentY = 0;
}
if (currentY > canvas.height) {
currentY = canvas.height;
}
path.push({ x: currentX, y: currentY });
}
return path;
}
function initMap() {
ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height); // 배경 이미지 그리기
drawPath();
}
function drawPath() {
const segmentLength = 20; // 몬스터 경로 세그먼트 길이
const imageWidth = 60; // 몬스터 경로 이미지 너비
const imageHeight = 60; // 몬스터 경로 이미지 높이
const gap = 5; // 몬스터 경로 이미지 겹침 방지를 위한 간격
for (let i = 0; i < monsterPath.length - 1; i++) {
const startX = monsterPath[i].x;
const startY = monsterPath[i].y;
const endX = monsterPath[i + 1].x;
const endY = monsterPath[i + 1].y;
const deltaX = endX - startX;
const deltaY = endY - startY;
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // 피타고라스 정리로 두 점 사이의 거리를 구함 (유클리드 거리)
const angle = Math.atan2(deltaY, deltaX); // 두 점 사이의 각도는 tan-1(y/x)로 구해야 함 (자세한 것은 역삼각함수 참고): 삼각함수는 변의 비율! 역삼각함수는 각도를 구하는 것!
for (let j = gap; j < distance - gap; j += segmentLength) {
// 사실 이거는 삼각함수에 대한 기본적인 이해도가 있으면 충분히 이해하실 수 있습니다.
// 자세한 것은 https://thirdspacelearning.com/gcse-maths/geometry-and-measure/sin-cos-tan-graphs/ 참고 부탁해요!
const x = startX + Math.cos(angle) * j; // 다음 이미지 x좌표 계산(각도의 코사인 값은 x축 방향의 단위 벡터 * j를 곱하여 경로를 따라 이동한 x축 좌표를 구함)
const y = startY + Math.sin(angle) * j; // 다음 이미지 y좌표 계산(각도의 사인 값은 y축 방향의 단위 벡터 * j를 곱하여 경로를 따라 이동한 y축 좌표를 구함)
drawRotatedImage(pathImage, x, y, imageWidth, imageHeight, angle);
}
}
}
function drawRotatedImage(image, x, y, width, height, angle) {
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
ctx.rotate(angle);
ctx.drawImage(image, -width / 2, -height / 2, width, height);
ctx.restore();
}
function getRandomPositionNearPath(maxDistance) {
// 타워 배치를 위한 몬스터가 지나가는 경로 상에서 maxDistance 범위 내에서 랜덤한 위치를 반환하는 함수!
const segmentIndex = Math.floor(Math.random() * (monsterPath.length - 1));
const startX = monsterPath[segmentIndex].x;
const startY = monsterPath[segmentIndex].y;
const endX = monsterPath[segmentIndex + 1].x;
const endY = monsterPath[segmentIndex + 1].y;
const t = Math.random();
const posX = startX + t * (endX - startX);
const posY = startY + t * (endY - startY);
const offsetX = (Math.random() - 0.5) * 2 * maxDistance;
const offsetY = (Math.random() - 0.5) * 2 * maxDistance;
return {
x: posX + offsetX,
y: posY + offsetY,
};
}
function placeInitialTowers() {
/*
타워를 초기에 배치하는 함수입니다.
무언가 빠진 코드가 있는 것 같지 않나요?
*/
for (let i = 0; i < numOfInitialTowers; i++) {
const { x, y } = getRandomPositionNearPath(200);
const tower = new Tower(x, y, towerCost);
towers.push(tower);
tower.draw(ctx, towerImage);
}
}
function placeNewTower() {
/*
타워를 구입할 수 있는 자원이 있을 때 타워 구입 후 랜덤 배치하면 됩니다.
빠진 코드들을 채워넣어주세요!
*/
const { x, y } = getRandomPositionNearPath(200);
const tower = new Tower(x, y);
towers.push(tower);
tower.draw(ctx, towerImage);
}
function placeBase() {
const lastPoint = monsterPath[monsterPath.length - 1];
base = new Base(lastPoint.x, lastPoint.y, baseHp);
base.draw(ctx, baseImage);
}
function spawnMonster() {
monsters.push(new Monster(monsterPath, monsterImages, monsterLevel));
}
function gameLoop() {
// 렌더링 시에는 항상 배경 이미지부터 그려야 합니다! 그래야 다른 이미지들이 배경 이미지 위에 그려져요!
ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height); // 배경 이미지 다시 그리기
drawPath(monsterPath); // 경로 다시 그리기
ctx.font = "25px Times New Roman";
ctx.fillStyle = "skyblue";
ctx.fillText(`최고 기록: ${highScore}`, 100, 50); // 최고 기록 표시
ctx.fillStyle = "white";
ctx.fillText(`점수: ${score}`, 100, 100); // 현재 스코어 표시
ctx.fillStyle = "yellow";
ctx.fillText(`골드: ${userGold}`, 100, 150); // 골드 표시
ctx.fillStyle = "black";
ctx.fillText(`현재 레벨: ${monsterLevel}`, 100, 200); // 최고 기록 표시
// 타워 그리기 및 몬스터 공격 처리
towers.forEach((tower) => {
tower.draw(ctx, towerImage);
tower.updateCooldown();
monsters.forEach((monster) => {
const distance = Math.sqrt(
Math.pow(tower.x - monster.x, 2) + Math.pow(tower.y - monster.y, 2)
);
if (distance < tower.range) {
tower.attack(monster);
}
});
});
// 몬스터가 공격을 했을 수 있으므로 기지 다시 그리기
base.draw(ctx, baseImage);
for (let i = monsters.length - 1; i >= 0; i--) {
const monster = monsters[i];
if (monster.hp > 0) {
const isDestroyed = monster.move(base);
if (isDestroyed) {
/* 게임 오버 */
alert("게임 오버. 스파르타 본부를 지키지 못했다...ㅠㅠ");
location.reload();
}
monster.draw(ctx);
} else {
/* 몬스터가 죽었을 때 */
monsters.splice(i, 1);
}
}
requestAnimationFrame(gameLoop); // 지속적으로 다음 프레임에 gameLoop 함수 호출할 수 있도록 함
}
function initGame() {
if (isInitGame) {
return;
}
monsterPath = generateRandomMonsterPath(); // 몬스터 경로 생성
initMap(); // 맵 초기화 (배경, 몬스터 경로 그리기)
placeInitialTowers(); // 설정된 초기 타워 개수만큼 사전에 타워 배치
placeBase(); // 기지 배치
setInterval(spawnMonster, monsterSpawnInterval); // 설정된 몬스터 생성 주기마다 몬스터 생성
gameLoop(); // 게임 루프 최초 실행
isInitGame = true;
}
// 이미지 로딩 완료 후 서버와 연결하고 게임 초기화
Promise.all([
new Promise((resolve) => (backgroundImage.onload = resolve)),
new Promise((resolve) => (towerImage.onload = resolve)),
new Promise((resolve) => (baseImage.onload = resolve)),
new Promise((resolve) => (pathImage.onload = resolve)),
...monsterImages.map(
(img) => new Promise((resolve) => (img.onload = resolve))
),
]).then(() => {
/* 서버 접속 코드 (여기도 완성해주세요!) */
let somewhere;
serverSocket = io("http://localhost:3020", {
auth: {
token: somewhere, // 토큰이 저장된 어딘가에서 가져와야 합니다!
},
});
/*
서버의 이벤트들을 받는 코드들은 여기다가 쭉 작성해주시면 됩니다!
e.g. serverSocket.on("...", () => {...});
이 때, 상태 동기화 이벤트의 경우에 아래의 코드를 마지막에 넣어주세요! 최초의 상태 동기화 이후에 게임을 초기화해야 하기 때문입니다!
if (!isInitGame) {
initGame();
}
*/
});
const buyTowerButton = document.createElement("button");
buyTowerButton.textContent = "타워 구입";
buyTowerButton.style.position = "absolute";
buyTowerButton.style.top = "10px";
buyTowerButton.style.right = "10px";
buyTowerButton.style.padding = "10px 20px";
buyTowerButton.style.fontSize = "16px";
buyTowerButton.style.cursor = "pointer";
buyTowerButton.addEventListener("click", placeNewTower);
document.body.appendChild(buyTowerButton);
위 코드를 Game Class로 변환했다.
import UserSocket from "../Network/userSocket.js";
import { Tower } from "../tower.js";
import pathManager from "../path.js";
import { Monster } from "../monster.js";
import { Inhibitor } from "../base.js";
import Player from "../player.js";
import { getLocalStorage } from "../Local/localStorage.js";
class Game {
constructor() {
this.canvas = document.getElementById("gameCanvas");
this.ctx = this.canvas.getContext("2d");
this.player = new Player(this.ctx, 60, 60); // 플레이어
this.userGold = 0; // 유저 돈
this.inhibitor = null; // 억제기
this.inhibitorHp = 0; // 억제기 체력
this.towerCost = 0; // 타워 구입시 가격
this.numOfInitialTowers = 0; // 게임 시작시 타워 자동 생성 -> 없어도 됨
this.monsterLevel = 1; // 몬스터 레벨
this.monsterSpawnInterval = 1000; // 몬스터 스폰시간
this.monsters = []; // 몬스터 저장 배열
this.towers = []; // 타워 저장 배열
this.score = 0; // 현재 플레이어의 스코어
this.highScore = 0; // 현재 서버 최고 스코어
this.backgroundImage = null; // 배경 이미지
this.towerImage = null; // 타워 이미지
this.inhibitorImage = null; // 억제기 이미지
this.pathImage = null; // 경로 이미지
this.monsterImages = []; // 몬스터 이미지
this.NUM_OF_MONSTERS = 5;
this.monsterPath = null; // 몬스터가 지나가는 경로
this.path = null; // 경로
//this.monsterPath = null; => 중첩된 거 같아서 주석처리 했습니다.
this.stageChange = true;
this.startTime = 0; // 게임 시작 시간
this.elpsedTime = 0; // 게임 종료 시간
this.buyTowerButton = document.createElement("button");
this.buyTowerButton.textContent = "타워 구입";
this.buyTowerButton.style.position = "absolute";
this.buyTowerButton.style.top = "10px";
this.buyTowerButton.style.right = "10px";
this.buyTowerButton.style.padding = "10px 20px";
this.buyTowerButton.style.fontSize = "16px";
this.buyTowerButton.style.cursor = "pointer";
this.buyTowerButton.addEventListener("click", () => {
const newTower = new Tower(this.player.x, this.player.y);
this.towers.push(newTower);
});
document.body.appendChild(this.buyTowerButton);
}
InitMap() {
this.ctx.drawImage(
this.backgroundImage,
0,
0,
this.canvas.width,
this.canvas.height
);
this.path.drawPath(this.monsterPath);
}
InitGame() {
this.monsterPath = this.path.generateRandomMonsterPath();
this.InitMap();
this.PlaceInitialTowers();
this.placeinhibitor();
}
PlaceInitialTowers() {
for (let i = 0; i < this.numOfInitialTowers; i++) {
const { x, y } = this.path.getRandomPositionNearPath(
200,
this.monsterPath
);
const tower = new Tower(x, y, this.towerCost);
this.towers.push(tower);
tower.draw(this.ctx, this.towerImage);
}
}
placeinhibitor() {
const lastPoint = this.monsterPath[this.monsterPath.length - 1];
this.inhibitor = new Inhibitor(lastPoint.x, lastPoint.y, this.inhibitorHp);
this.inhibitor.draw(this.ctx, this.inhibitorImage);
}
SpawnMonster() {
this.monsters.push(
new Monster(this.monsterPath, this.monsterImages, this.monsterLevel)
);
}
// 불러오는 정보가 추가되다보니 infos로 변경했습니다.
GameStart(infos) {
this.backgroundImage = infos.backgroundImage;
this.towerImage = infos.towerImage;
this.inhibitorImage = infos.inhibitorImage;
this.pathImage = infos.pathImage;
this.userGold = infos.userGold;
this.inhibitorHp = infos.inhibitorHp;
this.highScore = infos.highScore;
this.startTime = infos.startTime;
this.elpsedTime = infos.elpsedTime;
this.monsterImages = infos.monsterImages;
this.path = new pathManager(this.canvas, this.ctx, this.pathImage, 60, 60);
UserSocket.GetInstance().SendEvent(1, {});
this.InitGame();
}
async GameLoop() {
this.ctx.drawImage(
this.backgroundImage,
0,
0,
this.canvas.width,
this.canvas.height
);
this.path.drawPath(this.monsterPath);
this.player.draw();
this.elpsedTime++;
// 현재 id가 마지막 스테이지일 때 스테이지 변경 금지
if (
getLocalStorage("currentStage").id ===
getLocalStorage("stages")[getLocalStorage("stages").length - 1].id
) {
this.stageChange = false;
}
// 일정 시간이 지날 경우 스테이집 ㅕㄴ경
if ((this.elpsedTime - this.startTime) % 500 === 0 && this.stageChange) {
UserSocket.GetInstance().SendEvent(2, {
currentStage: getLocalStorage("currentStage"),
});
}
this.ctx.font = "25px Times New Roman";
this.ctx.fillStyle = "skyblue";
this.ctx.fillText(`최고 기록: ${this.highScore}`, 100, 50); // 최고 기록 표시
this.ctx.fillStyle = "white";
this.ctx.fillText(`점수: ${this.score}`, 100, 100); // 현재 스코어 표시
this.ctx.fillStyle = "yellow";
this.ctx.fillText(`골드: ${this.userGold}`, 100, 150); // 골드 표시
this.ctx.fillStyle = "red";
this.ctx.fillText(
`현재 스테이지: ${getLocalStorage("currentStage").id}`,
100,
200
); // 현재 스테이지 표시
this.towers.forEach((tower) => {
tower.draw(this.ctx, this.towerImage);
tower.updateCooldown();
tower.singleAttack(tower, this.monsters); // 단일 공격
tower.multiAttack(tower, this.monsters); // 다중 공격
tower.heal(tower, this.inhibitor); // 힐
});
this.inhibitor.draw(this.ctx, this.inhibitorImage);
for (let i = this.monsters.length - 1; i >= 0; i--) {
const monster = this.monsters[i];
if (monster.hp > 0) {
const isDestroyed = monster.move(this.inhibitor);
if (isDestroyed) {
/* 게임 오버 */
alert("게임 오버. 스파르타 본부를 지키지 못했다...ㅠㅠ");
location.reload();
}
monster.draw(this.ctx);
} else {
/* 몬스터가 죽었을 때 */
this.monsters.splice(i, 1);
}
}
requestAnimationFrame(() => {
this.GameLoop();
});
}
}
export default Game;
prisma에 관한 이슈가 있었는데, 다행히도 해결했다.
이슈는 다음과 같다.
팀 프로젝트에서 prisma 스키마를 총 2가지 사용 한다.
user 테이블
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL_USER")
}
model user {
id Int @id @default(autoincrement()) @map("id")
highScore Int @map("highScore")
password String @map("password")
email String @map("email")
@@map("User")
}
Asset 테이블
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL_ASSET")
}
enum attackType {
singleAttack
multiAttack
heal
}
model initGame {
id Int @id @default(autoincrement()) @map("Id")
baseHp Int @map("baseHp")
startGold Int @map("startGold")
serverHighScore Int @map("serverHighScore")
@@map("InitGame")
}
model monster {
id Int @id @default(autoincrement()) @map("Id")
hp Int @map("hp")
atk Int @map("atk")
speed Int @map("speed")
score Int @map("score")
gold Int @map("stargoldtGold")
stage Int @map("stage")
grade Int @map("servgradeerHighScore")
@@map("Monster")
}
model tower {
id Int @id @default(autoincrement()) @map("Id")
towerName String @map("towerName")
attackPower Int @map("attackPower")
attackSpeed Int @map("attackSpeed")
attackRange Int @map("attackRange")
attackType attackType @map("attackType")
towerPrice Int @map("towerPrice")
upgradeAttackPower Int @map("upgradeAttackPower")
@@map("Tower")
}
위처럼 구성을 하고
npx prisma generate --schema=prisma/schemaAsset.prisma
npx prisma generate --schema=prisma/schemaUser.prisma 를 통해 각각 prismaClient를 생성해줬다.
import { PrismaClient } from "@prisma/client";
import dotenv from "dotenv";
dotenv.config();
export const prismaUser = new PrismaUserClient({
log: ["query", "info", "warn", "error"],
errorFormat: "pretty",
datasources: {
db: {
url: process.env.DATABASE_URL_USER,
},
},
});
export const prismaAsset = new PrismaAssetClient({
log: ["query", "info", "warn", "error"],
errorFormat: "pretty",
datasources: {
db: {
url: process.env.DATABASE_URL_ASSET,
},
},
});
위처럼, 따로 파일을 만들어 User 테이블 PrismaClient와 Asset 테이블 PrismaClient에 접근할 수 있도록 구성했다.
User 테이블에 접근할 때는 문제가 없었는데,
Asset 테이블에 접근하면 undefined이 뜨는 문제가 생겼다.
이유를 찾지 못해, 몇시간 동안 이유를 찾아보다가 결국 찾았는데,
node_moudles 안에 있는 index.js에서 원인을 찾았다.
@prisma/client ( node_modules/client ) 으로 가서 안에 있는 파일들을 살펴보니
index.js에서 PrismaClient를 가지고오는 것을 확인할 수 있었다.
문제는 index.js에 User 테이블에 관한 기록만 생성이 되어있고, Asset 테이블에 관한 기록이 없었다는 점이다.
Object.defineProperty(exports, "__esModule", { value: true });
const {
PrismaClientKnownRequestError,
PrismaClientUnknownRequestError,
PrismaClientRustPanicError,
PrismaClientInitializationError,
PrismaClientValidationError,
NotFoundError,
getPrismaClient,
sqltag,
empty,
join,
raw,
skip,
Decimal,
Debug,
objectEnumValues,
makeStrictEnum,
Extensions,
warnOnce,
defineDmmfProperty,
Public,
getRuntime
} = require('@prisma/client/runtime/library.js')
const Prisma = {}
exports.Prisma = Prisma
exports.$Enums = {}
/**
* Prisma Client JS version: 5.20.0
* Query Engine version: 06fc58a368dc7be9fbbbe894adf8d445d208c284
*/
Prisma.prismaVersion = {
client: "5.20.0",
engine: "06fc58a368dc7be9fbbbe894adf8d445d208c284"
}
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
Prisma.PrismaClientValidationError = PrismaClientValidationError
Prisma.NotFoundError = NotFoundError
Prisma.Decimal = Decimal
/**
* Re-export of sql-template-tag
*/
Prisma.sql = sqltag
Prisma.empty = empty
Prisma.join = join
Prisma.raw = raw
Prisma.validator = Public.validator
/**
* Extensions
*/
Prisma.getExtensionContext = Extensions.getExtensionContext
Prisma.defineExtension = Extensions.defineExtension
/**
* Shorthand utilities for JSON filtering
*/
Prisma.DbNull = objectEnumValues.instances.DbNull
Prisma.JsonNull = objectEnumValues.instances.JsonNull
Prisma.AnyNull = objectEnumValues.instances.AnyNull
Prisma.NullTypes = {
DbNull: objectEnumValues.classes.DbNull,
JsonNull: objectEnumValues.classes.JsonNull,
AnyNull: objectEnumValues.classes.AnyNull
}
const path = require('path')
/**
* Enums
*/
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
});
exports.Prisma.UserScalarFieldEnum = {
id: 'id',
highScore: 'highScore',
password: 'password',
email: 'email'
};
exports.Prisma.SortOrder = {
asc: 'asc',
desc: 'desc'
};
exports.Prisma.ModelName = {
user: 'user'
};
/**
* Create the Client
*/
const config = {
"generator": {
"name": "client",
"provider": {
"fromEnvVar": null,
"value": "prisma-client-js"
},
"output": {
"value": "C:\\Node.js Project\\TowerDefence\\node_modules\\@prisma\\client",
"fromEnvVar": null
},
"config": {
"engineType": "library"
},
"binaryTargets": [
{
"fromEnvVar": null,
"value": "windows",
"native": true
}
],
"previewFeatures": [],
"sourceFilePath": "C:\\Node.js Project\\TowerDefence\\prisma\\schemaUser.prisma"
},
"relativeEnvPaths": {
"rootEnvPath": null,
"schemaEnvPath": "../../../.env"
},
"relativePath": "../../../prisma",
"clientVersion": "5.20.0",
"engineVersion": "06fc58a368dc7be9fbbbe894adf8d445d208c284",
"datasourceNames": [
"db"
],
"activeProvider": "mysql",
"postinstall": false,
"inlineDatasources": {
"db": {
"url": {
"fromEnvVar": "DATABASE_URL_USER",
"value": null
}
}
},
"inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"mysql\"\n url = env(\"DATABASE_URL_USER\")\n}\n\nmodel user {\n id Int @id @default(autoincrement()) @map(\"id\")\n highScore Int @map(\"highScore\")\n password String @map(\"password\")\n email String @map(\"email\")\n\n @@map(\"User\")\n}\n",
"inlineSchemaHash": "b41a0a6e2263d480ccedf76c8119c75ebcb8d58ab449c6161959952662e00187",
"copyEngine": true
}
const fs = require('fs')
config.dirname = __dirname
if (!fs.existsSync(path.join(__dirname, 'schema.prisma'))) {
const alternativePaths = [
"node_modules/.prisma/client",
".prisma/client",
]
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, 'schema.prisma'))
}) ?? alternativePaths[0]
config.dirname = path.join(process.cwd(), alternativePath)
config.isBundled = true
}
config.runtimeDataModel = JSON.parse("{\"models\":{\"user\":{\"dbName\":\"User\",\"fields\":[{\"name\":\"id\",\"dbName\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":{\"name\":\"autoincrement\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"highScore\",\"dbName\":\"highScore\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"password\",\"dbName\":\"password\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"dbName\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{},\"types\":{}}")
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
config.engineWasm = undefined
const { warnEnvConflicts } = require('@prisma/client/runtime/library.js')
warnEnvConflicts({
rootEnvPath: config.relativeEnvPaths.rootEnvPath && path.resolve(config.dirname, config.relativeEnvPaths.rootEnvPath),
schemaEnvPath: config.relativeEnvPaths.schemaEnvPath && path.resolve(config.dirname, config.relativeEnvPaths.schemaEnvPath)
})
const PrismaClient = getPrismaClient(config)
exports.PrismaClient = PrismaClient
Object.assign(exports, Prisma)
// file annotations for bundling tools to include these files
path.join(__dirname, "query_engine-windows.dll.node");
path.join(process.cwd(), "node_modules/.prisma/client/query_engine-windows.dll.node")
// file annotations for bundling tools to include these files
path.join(__dirname, "schema.prisma");
path.join(process.cwd(), "node_modules/.prisma/client/schema.prisma")
위 코드를 살펴보면 User 테이블에 관한 기록은 되어 있지만, Asset 테이블에 관한 기록은 없는 것을 확인할 수 있다.
따라서 PrismaClient를 각각 따로 생성해줘야 해결이 될 것 같다라는 생각을 했다.
generator client {
provider = "prisma-client-js"
}
prisma 에서 table을 구성하는 파일인 .prima 파일의 generator client 에서
output 기능을 사용하면 각각의 PrismaClient를 생성해주는 것을 찾았다.
즉 위 코드를
generator client {
provider = "prisma-client-js"
output = "../prisma/assetPrisma/client"
}
generator client {
provider = "prisma-client-js"
output = "../prisma/userPrisma/client"
}
이렇게 생성하면
이렇게 node_modules 폴더에 생성하지 않고, 내가 직접 지정해준 경로에 생성해준다는 것을 찾아서,
asset과 user를 각각 분리했다.
import { PrismaClient as PrismaUserClient } from "../../../../prisma/userPrisma/client/index.js";
import { PrismaClient as PrismaAssetClient } from "../../../../prisma/assetPrisma/client/index.js";
import dotenv from "dotenv";
dotenv.config();
export const prismaUser = new PrismaUserClient({
log: ["query", "info", "warn", "error"],
errorFormat: "pretty",
datasources: {
db: {
url: process.env.DATABASE_URL_USER,
},
},
});
export const prismaAsset = new PrismaAssetClient({
log: ["query", "info", "warn", "error"],
errorFormat: "pretty",
datasources: {
db: {
url: process.env.DATABASE_URL_ASSET,
},
},
});
그리고 나서 PrismaClient에 접근 하는 코드에도 경로를 새로 지정해 사용하니 더이상은 에러가 나지 않았다.
'내일배움캠프' 카테고리의 다른 글
[내일배움캠프][TIL] 43일차 - 팀프로젝트 ( 타워디펜스 ) 완성 (0) | 2024.10.16 |
---|---|
[내일배움캠프][TIL] 42일차 - 팀 프로젝트 (1) | 2024.10.15 |
[내일배움캠프][TIL] 40일차 - 팀프로젝트 회의 (0) | 2024.10.09 |
[내일배움캠프][TIL] 39일차 - 개인과제 (0) | 2024.10.08 |
[내일배움캠프][TIL] 38일차 - 개인 과제 (0) | 2024.10.02 |