[PWA] (30) prisma db연결중 - 로그인 api

Kimmy·2025년 6월 9일

PWA_PROJECT

목록 보기
42/47

MySQL Workbench에 테이블만 prisma로 만들어둔 상태에서, localStorage 대신 api코드로 수정해보았다.

해야할 것

  1. MySQL user테이블에 더미데이터 insert 하기
  • localStorage의 user키값중 하나 참고하기
7: {id: "blue", password: "test1234!", name: "김블루", phone: "01000000000", email: "blue@blue.com",…}
adminBusinessFile: null
adminPhone: null
email: "blue@blue.com"
id: "blue"
isAdmin: false
name: "김블루"
password: "test1234!"
phone: "01000000000"
points: 0
  1. LoginModal.js
    handleLogin 함수에서 localStorage관련 코드를 모두 api 코드로 수정하기

  2. /api/login.js 새로 만들고 백엔드 로직 생성하기

prisma 와 MySQL 테이블 컬럼 불일치해도 되는지?

결론먼저, 괜찮다!
Prisma의 User 모델에 정의된 컬럼과 MySQL에서 SELECT로 조회되는 컬럼이 다르게 보일 수 있는 이유는,

Prisma 모델에 정의된 reservations, points, reviews, notifications, recentSearches 등은 실제로는 User 테이블의 컬럼이 아니라, 다른 테이블과의 관계(1:N, N:M 등)를 나타내는 Prisma의 가상 필드이기 때문이다.

MySQL의 user 테이블에는 실제로 존재하는 컬럼(즉, id, createdAt, updatedAt, userId, pwd, name, email, phone, isAdmin, adminPhoneNumber, adminBusinessFile)만 보인다.
즉, MySQL에서 SELECT로 조회되는 컬럼이 Prisma 모델의 실제 DB 컬럼과 일치하면 정상이다.
Prisma의 관계 필드는 실제 DB 컬럼이 아니므로, MySQL에서 직접 SELECT 할 때는 보이지 않는 것이 맞다.

✔️정리:
MySQL에서 보이는 컬럼 = 실제 테이블 컬럼
Prisma에서 보이는 관계 필드 = 실제 컬럼이 아니라 관계(연결) 정보

Error Code: 1364. Field 'updatedAt' doesn't have a default value 에러

autoincrement 인 id 값과, 자동으로 생성되는 createdAt, updatedAt은 빼고 나머지값으로만 user를 한개 생성하려고 하는데,

INSERT INTO User (userId, name, email, phone, isAdmin, pwd)
VALUES ('blue', '김블루', 'blue@blue.com', '01000000000', false, 'test1234!');

updatedAt의 기본값이 없다는 에러가 났다. 그런데 updatedAt는 update 될때만 필요한 값으로, 기본값이라는게 필요없는것 아닌가 싶었는데 찾아보니,
Prisma에서 DateTime 타입은 기본적으로 NOT NULL이라고한다.

Prisma에서는 @updatedAt을 사용하면 Prisma Client로 데이터를 수정할 때 자동으로 값을 넣어주지만,
지금처럼 MySQL에서 직접 INSERT할 때는 updatedAt 값을 명시적으로 넣어줘야 한다.

  • 게시글을 처음 올릴 때: createdAt과 updatedAt이 같은 값으로 자동 저장됨 (NULL 아님)
  • 게시글을 수정할 때: updatedAt이 자동으로 갱신됨
  • 수정하지 않아도 updatedAt은 항상 값이 있음 (NULL이 아님)

🪻해결 방법

  1. INSERT문에 updatedAt 컬럼에 NOW() 같은 값을 추가
INSERT INTO User (userId, name, email, phone, isAdmin, pwd, updatedAt)
VALUES ('blue', '김블루', 'blue@blue.com', '01000000000', false, 'test1234!', NOW());

로그인 부터 db 화 시키기

✔️1.loginModal.js

handleLogin 함수에서 로그인 로직관련 localStorage 코드를 모두 api 코드로 수정하기

수정전) localStorage 코드

export default function LoginModal({ onClose }) {
  const [id, setId] = useState("");
  const [password, setPassword] = useState("");
  const [isSignUpModalOpen, setIsSignUpModalOpen] = useState(false);

  const handleLogin = () => {
    const users = JSON.parse(localStorage.getItem("users") || "[]");
    const user = users.find((u) => u.id === id && u.password === password);

    if (user) {
      // 로그인 성공
      localStorage.setItem("currentUser", JSON.stringify(user));
      alert("로그인되었습니다.");
      onClose();
      window.location.reload(); // 헤더 상태 갱신을 위한 새로고침
    } else {
      alert("아이디 또는 비밀번호가 일치하지 않습니다.");
    }
  };

수정후) api 코드

const handleLogin = async () => {
  try {
    const res = await fetch("/api/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ userId: id, pwd: password }),
    });
    if (res.ok) {
      const user = await res.json();
      // 로그인 성공 처리 (예: context, 전역 상태 등)
      alert("로그인되었습니다.");
      onClose();
      window.location.reload();
    } else {
      alert("아이디 또는 비밀번호가 일치하지 않습니다.");
    }
  } catch (err) {
    alert("로그인 중 오류가 발생했습니다.");
  }
};
body: JSON.stringify({ userId: id, pwd: password })

이 코드는 프론트엔드에서 로그인 시 입력한 아이디와 비밀번호를 서버로 보내기 위해, fetch의 body에 아래와 같이 JSON 형태로 데이터를 담는 부분 :
id와 password는 각각 로그인 폼의 입력값을 담고 있는 상태(state) 변수.

const [id, setId] = useState("");
const [password, setPassword] = useState("");

이 값들을 서버에 보낼 때는
프론트의 state 변수(id, password)의 값을
서버가 원하는 필드명(userId, pwd)으로 맞춰서 보내는 것.

✔️2. /api/login

엔드포인트가 실제로 동작하도록 백엔드도 구현하기.

import prisma from '../../../lib/prisma';

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: 'Method not allowed' });
  }

  const { userId, pwd } = req.body;

  if (!userId || !pwd) {
    return res.status(400).json({ message: '아이디와 비밀번호를 입력하세요.' });
  }

  try {
    const user = await prisma.user.findUnique({
      where: { userId },
    });

    if (!user || user.pwd !== pwd) {
      return res.status(401).json({ message: '아이디 또는 비밀번호가 일치하지 않습니다.' });
    }

    // 비밀번호는 응답에서 제외
    const { pwd: _, ...userInfo } = user;
    return res.status(200).json(userInfo);
  } catch (error) {
    return res.status(500).json({ message: '서버 오류', error: error.message });
  }
}

아디 비번 틀림

에러 1.

POST http://localhost:3000/api/login 500 (Internal Server Error)

DB에 userId='blue'인 유저가 실제로 있는지 확인
Prisma schema와 DB 컬럼명이 일치하는지 확인
해봤지만 아무 문제 없었다.

에러 2.

TypeError: Cannot read properties of undefined (reading 'findUnique')

prisma.user가 undefined라는 뜻.
즉, Prisma Client가 User 모델을 인식하지 못하고 있다.

import { prisma } from "../../../lib/prisma";
이 코드로 불러온 prisma가 제대로 생성되지 않았다는 뜻.

@prisma/client가 오래된 버전이거나, generate된 코드가 꼬였을 때 발생할 수 있다고 한다.

해결 방법 (순서중요)

node_modules, .prisma, .next, src/generated/prisma 모두 삭제
@prisma/client, prisma 재설치
prisma generate 재실행
서버 재시작

rm -rf node_modules .prisma .next src/generated/prisma package-lock.json

까지하니, 에러 3 발생.

에러 3.

@prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.

즉, Prisma Client가 아직 생성되지 않았거나, Next.js가 빌드할 때 @prisma/client의 코드가 누락된 경우.

해결 방법 ->
prisma generate를 src/generated/prisma가 아닌 기본 위치로 변경

generator client {
  provider = "prisma-client-js"
  // output   = "../src/generated/prisma"
}

-> schema.prisma에서 generator의 output 라인을삭제
= 기본값으로 변경
-> prisma generate 명령어가 Prisma Client를 node_modules/@prisma/client에 생성한다.

아래 명령어를 순서대로 실행(서버 재시작)

npx prisma generate
npm run dev

결론 : 로그인 성공

profile
바리바리 개바리 🌼

0개의 댓글