본문 바로가기
내일배움 강의/강의- Node.js 입문, 숙련

Node.js 숙련주차 4.1 인증(Authentication), 인가(Authorization) - 나만의 게시판 사이트

by GREEN나무 2024. 12. 2.
728x90

1. 인증(Authentication), 인가(Authorization) 개념에 대해 알아봅니다.
2. Prisma를 이용해 회원가입, 로그인 기능을 구현해봅니다.
3. bcrypt를 이용해 암호화된 비밀번호를 인증하는 방법을 알아봅니다.

 

01.인증 / 인가

1) 인증(Authentication)

인증(Authentication)은 일반적인 사이트의 로그인 기능에 해당합니다.
서비스를 이용하려는 사용자가 인증된 신분을 가진 사람이 맞는지 검증하는 작업을 뜻합니다.

참고 - OAuth : 오어스   (구글, 카톡 등의)플렛폼을 통한 로그인

2) 인가(Authorization)

인가(Authorization)는 이미 인증된 사용자가 특정 리소스에 접근하거나 특정 작업을 수행할 수 있는 권한이 있는지를 검증하는 작업
인가(Authorization)기능은 사용자 인증 미들웨어를 통해서 구현할 예정입니다.

 

02. 로그인, 회원가입

Prisma 게시판 프로젝트 API 명세서 :

https://teamsparta.notion.site/e226b48376d5430da163ec5a9a08ec98?v=3b302a655f85456ea3ec38d288cef440

더보기
기능 API URL Method Request
Header
Request Response Response
Header
회원가입 localhost:3018/api/sing-up POST   { "email": "archepro84@gmail.com", "password": "4321aaaa", "name": "이용우", "age": 30, "gender": "MALE", "profileImage": "https://prismalens.vercel.app/header/logo-dark.svg" } { "message": "회원가입이 완료되었습니다." }  
로그인 localhost:3018/api/sing-in POST   { "email":"archepro84@gmail.com", "password":"4321aaaa" } { "message": "로그인 성공" } { “authorization”: “Bearer eyJhbGciOiJIUzI1NiIsIn…” }
사용자
조회
localhost:3018/api/users GET { authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTY5MTA1NTc5OH0.2bM9s-Vv312rgkRzQiWLy4ASa0lWk5TOEGlEvNOa67k' } { } { "data": { "userId": 1, "email": "archepro84@gmail.com", "createdAt": "2024-01-03T13:57:48.058Z", "updatedAt": "2024-01-03T13:57:48.058Z", "userInfos": { "name": "이용우", "age": 30, "gender": "MALE", "profileImage": "https://prismalens.vercel.app/header/logo-dark.svg" } } }  

 

1. 프로젝트 초기화

모든 Router에서 Prisma를 사용할 수 있도록 '프로젝트폴더\src\utils\prisma\index.js ' 파일 만들기

 

 // package.json에 추가
 "type": "module",  
  "scripts": {
    "dev": "nodemon ./src/app.js"
  },
// src/app.js

import express from 'express';
import cookieParser from 'cookie-parser';

const app = express();
const PORT = 3018;

app.use(express.json());
app.use(cookieParser());

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});
// src/utils/prisma/index.js

import { PrismaClient } from '@prisma/client';

export const prisma = new PrismaClient({
  // Prisma를 이용해 데이터베이스를 접근할 때, SQL을 출력해줍니다.
  log: ['query', 'info', 'warn', 'error'],

  // 에러 메시지를 평문이 아닌, 개발자가 읽기 쉬운 형태로 출력해줍니다.
  errorFormat: 'pretty',
}); // PrismaClient 인스턴스를 생성합니다.

 

2. 회원가입 API

[게시판 프로젝트] 회원가입 API 비즈니스 로직

1. email, password, name, age, gender, profileImagebody로 전달받습니다.
2. 동일한 email을 가진 사용자가 있는지 확인합니다. (unique)
3. Users 테이블email, password를 이용해 사용자를 생성합니다.
4. UserInfos 테이블name, age, gender, profileImage를 이용해 사용자 정보를 생성합니다.

 

회원가입 API는 사용자와 사용자 정보가 1:1 관계를 가진 것을 바탕으로 비즈니스 로직이 구현됩니다.

사용자 정보는 사용자가 존재하지 않을 경우 생성될 수 없으므로, 전달받은 인자값을 이용해 사용자 → 사용자 정보 순서대로 회원가입을 진행해야합니다.

 

그러면 routes/users.router.js 파일을 생성하고, UsersRouter를 app.js전역 미들웨어에 등록 후 회원가입 API를 구현 하겠습니다

 

// app.js

import express from "express";
import cookieParser from "cookie-parser";
import UsersRouter from "./routes/users.router.js"; //

const app = express();
const PORT = 3018;

app.use(express.json());
app.use(cookieParser());
app.use("/api", [UsersRouter]); //

app.listen(PORT, () => {
  console.log(PORT, "포트로 서버가 열렸어요!");
});

 

// src/routes/users.router.js

import express from "express";
import { prisma } from "../utils/prisma/index.js";

const router = express.Router();

// [게시판 프로젝트] 회원가입 API 비즈니스 로직
// 1. email, password, name, age, gender, profileImage를 body로 전달받습니다.
// 2. 동일한 email을 가진 사용자가 있는지 확인합니다.
// 3. Users 테이블에 email, password를 이용해 사용자를 생성합니다.
// 4. UserInfos 테이블에 name, age, gender, profileImage를 이용해 사용자 정보를 생성합니다.

/** 사용자 회원가입 API **/
router.post("/sign-up", async (req, res, next) => {
  // 1. email, password, name, age, gender, profileImage를 body로 전달받습니다.
  const { email, password, name, age, gender, profileImage } = req.body;
  // 2. 동일한 email을 가진 사용자가 있는지 확인합니다.
  const isExistUser = await prisma.users.findFirst({
    where: {
      email,
    },
  });

  if (isExistUser) {
    // 이미 있는 이메일 사용
    return res.status(409).json({ message: "이미 존재하는 이메일입니다." });
  }

  // 3. Users 테이블에 email, password를 이용해 사용자를 생성합니다.
  const user = await prisma.users.create({
    data: { email, password },
  });

  // 4. UserInfos 테이블에 name, age, gender, profileImage를 이용해 사용자 정보를 생성합니다.
  const userInfo = await prisma.userInfos.create({
    data: {
      userId: user.userId, // 여기 'user'는 3.에서 const user한 변수. 생성한 유저의 userId를 바탕으로 사용자 정보를 생성합니다.
      name,
      age,
      gender: gender.toUpperCase(), // 성별을 대문자로 변환합니다.
      profileImage,
    },
  });

  return res.status(201).json({ message: "회원가입이 완료되었습니다." });
});

export default router;

 

터미널에서 실행하기

# 이 중에 하나로 실행하세요
npx nodemon src/app.js
npx yarn dev
yarn dev

 

insomnia로 회원가입하기

동일한 body로 다시 send      설정한 오류가 정상적으로 발생.

 

❓ 사용자만 생성되고, 사용자 정보 생성에 실패하면?
현재는 Users 테이블에 사용자를 생성하고, UserInfos 테이블에 userId를 이용해 사용자의 정보를 저장합니다. 
만약, 사용자 정보 저장에 실패하게 될 경우 Users 테이블에는 사용자가 존재하지만, UserInfos 테이블의 사용자 정보는 저장되지 않는 상태로 남아있게됩니다.
이런 문제를 해결하기 위해서 MySQL과 같은 RDBMS에서는 트랜잭션(Transaction)이라는 개념이 도입되었습니다.

 

3) [게시판 프로젝트] bcrypt

일반적으로, 사용자의 비밀번호를 데이터베이스에 저장할 때, 보안을 위해 비밀번호를 평문으로 저장하지 않고 암호화 하여 저장합니다.
   → 개인정보보호법에서는 주민등록번호와 같은 정보는 암호화되어 저장되어야합니다.

 

bcrypt 모듈

bcrypt 모듈은 입력받은 데이터를 특정 암호화 알고리즘을 이용하여 암호화 및 검증을 도와주는 모듈입니다. 
sparta라는 비밀번호를 bcrypt를 이용해 암호화하게 된다면 특정한 문자열로 변환됩니다. 이 변환된 문자열은 단뱡향 암호화되어 원래의 비밀번호(sparta)로 복구할 수 없게됩니다.
하지만, 입력된 비밀번호가 암호화된 문자열과 일치하는지는 비교할 수 있습니다.
이를 통해 사용자의 비밀번호가 올바른지, 아닌지 검증할 수 있게 되는것이죠. 

   → 비밀번호는 모르지만 로그인, 인증 가능

bcrypt를 암호화, 복호화 하는 문법

bcrypt 암호화

import bcrypt from 'bcrypt';

const password = 'Sparta'; // 사용자의 비밀번호
const saltRounds = 10; // 몇번 암호화 할까? salt를 얼마나 복잡하게 만들지 결정합니다.

// 'hashedPassword'는 암호화된 비밀번호 입니다.
const hashedPassword = await bcrypt.hash(password, saltRounds);

console.log(hashedPassword); //$2b$10$OOziCKNP/dH1jd.Wvc3JluZVm7H8WXR8oUmxUQ/cfdizQOLjCXoXa


bcrypt 복호화

import bcrypt from 'bcrypt';

const password = 'Sparta'; // 사용자가 입력한 비밀번호
const hashed = '$2b$10$OOziCKNP/dH1jd.Wvc3JluZVm7H8WXR8oUmxUQ/cfdizQOLjCXoXa'; // DB에서 가져온 암호화된 비밀번호

// 'result'는 비밀번호가 일치하면 'true' 아니면 'false'
const result = await bcrypt.compare(password, hashed);

console.log(result); // true

// 비밀번호가 일치하지 않다면, 'false'
const failedResult = await bcrypt.compare('FailedPassword', hashed);

console.log(failedResult); // false

 


4) [게시판 프로젝트] 회원가입 API Bcrypt 리팩토링


회원 가입 API에서 비밀번호를 평문으로 저장하는 것을, bcrypt를 이용하여 단뱡항 암호화한 결과값으로 데이터베이스에 저장하도록 리팩토링 할 예정입니다.

 

bcrypt 설치

# yarn을 이용해 bcrypt를 설치합니다.
yarn add bcrypt


​❓ bcrypt 설치에 실패했어요
 /bin/sh: node-pre-gyp: command not found 에러 메시지가 출력된다면, 전역으로 node-pre-gyp 패키지를 설치하면 해결됩니다.
node-pre-gyp 패키지 전역 설치 명령어

# node-pre-gyp 패키지를 전역으로 설치합니다.
npm install -g node-pre-gyp


​회원가입 API Bcrpyt 리팩토링

// src/routes/users.router.js

import express from "express";
import bcrypt from "bcrypt";
import { prisma } from "../utils/prisma/index.js";

const router = express.Router();

/** 사용자 회원가입 API 리팩토링**/
router.post("/sign-up", async (req, res, next) => {
    const { email, password, name, age, gender, profileImage } = req.body;
    const isExistUser = await prisma.users.findFirst({
        where: {
            email,
        },
    });

    if (isExistUser) {
        return res.status(409).json({ message: "이미 존재하는 이메일입니다." });
    }

    // 사용자 비밀번호를 암호화합니다.
    const hashedPassword = await bcrypt.hash(password, 10);

    // Users 테이블에 사용자를 추가합니다.
    const user = await prisma.users.create({
        data: {
            email,
            password: hashedPassword, // 암호화된 비밀번호를 저장합니다.
        },
    });

    // UserInfos 테이블에 사용자 정보를 추가합니다.
    const userInfo = await prisma.userInfos.create({
        data: {
            userId: user.userId, // 생성한 유저의 userId를 바탕으로 사용자 정보를 생성합니다.
            name,
            age,
            gender: gender.toUpperCase(), // 성별을 대문자로 변환합니다.
            profileImage,
        },
    });

    return res.status(201).json({ message: "회원가입이 완료되었습니다." });
});
export default router;

 

5) [게시판 프로젝트] 로그인 API, 사용자 인증 미들웨어

로그인 API bcrypt로 암호화된 사용자 비밀번호를 인증하는 방식입니다. 클라이언트가 제공한 비밀번호와 데이터베이스에 저장된 암호화된 비밀번호를 검증하여 구현할 예정입니다.

 

[게시판 프로젝트]  로그인 API 비즈니스 로직

1. email, password를 body로 전달받습니다.
2 . 전달 받은 email에 해당하는 사용자가 있는지 확인합니다.
3 . 전달 받은 password와 데이터베이스의 저장된 password를 bcrypt를 이용해 검증합니다.
4 . 로그인에 성공한다면, 사용자에게 JWT를 발급합니다.

 

로그인 API는 클라이언트가 전달한 정보를 바탕으로 사용자를 확인합니다. 클라이언트로부터 받은 emailpassword를 사용하여 데이터베이스에 저장된 사용자를 검증하고, 검증에 성공하면 JWT를 담고있는 쿠키를 생성하여 반환하게됩니다.
클라이언트는 로그인 이후 요청부터 쿠키를 함께 보내면, 이를 통해 서버에서는 해당 사용자를 식별하고, 인증(Authentication), 인가(Authorization) 과정을 거칠 것 입니다.

 

[게시판 프로젝트] 로그인 API

// src/routes/users.route.js

import jwt from 'jsonwebtoken';

/** 로그인 API **/
router.post('/sign-in', async (req, res, next) => {
  const { email, password } = req.body;
  const user = await prisma.users.findFirst({ where: { email } });

  if (!user)
    return res.status(401).json({ message: '존재하지 않는 이메일입니다.' });
  // 입력받은 사용자의 비밀번호와 데이터베이스에 저장된 비밀번호를 비교합니다.
  else if (!(await bcrypt.compare(password, user.password)))
    return res.status(401).json({ message: '비밀번호가 일치하지 않습니다.' });

  // 로그인에 성공하면, 사용자의 userId를 바탕으로 토큰을 생성합니다.
  const token = jwt.sign(
    {
      userId: user.userId,
    },
    'custom-secret-key',
  );

  // authotization 쿠키에 Berer 토큰 형식으로 JWT를 저장합니다.
  res.cookie('authorization', `Bearer ${token}`);
  return res.status(200).json({ message: '로그인 성공' });
});

 bcrypt.compare()    https://www.npmjs.com/package/bcrypt

%20이 띄어쓰기(스페이스)를 의미


6) [게시판 프로젝트] 사용자 인증 미들웨어

사용자 인증 미들웨어클라이언트로 부터 전달받은 쿠키를 검증하는 작업을 수행합니다. 클라이언트가 제공한 쿠키에 담겨있는 JWT를 이용해 사용자를 조회하도록 구현할 예정입니다.

 

[게시판 프로젝트] 사용자 인증 미들웨어 비즈니스 로직
1. 클라이언트로 부터 쿠키(Cookie)를 전달받습니다.
2 . 쿠키(Cookie)가 Bearer 토큰 형식인지 확인합니다.
3 . 서버에서 발급한 JWT가 맞는지 검증합니다.
4 . JWT의 userId를 이용해 사용자를 조회합니다.
5 . req.user 에 조회된 사용자 정보를 할당합니다.
6 . 다음 미들웨어를 실행합니다.

 

사용자 인증 미들웨어는 클라이언트가 전달한 쿠키를 바탕으로 사용자를 검증합니다. 이 과정에서 토큰이 만료되진 않았는지, 토큰의 형식은 일치하는지, 서버가 발급한 토큰이 맞는지 등 다양한 검증을 수행하여, 사용자의 권한을 확인하는 것이죠.
이렇게, JWT를 통해 사용자를 인증하는 것을 인증(Authentication)이라고 부릅니다. ​
클라이언트는 인증 과정을 전부 통과하였을 때에만 로그인된 사용자만 사용할 수 있는 게시글 작성 API, 사용자 정보 조회 API와 같은 권한이 필요한 API를 사용할 수 있게 되는것이죠.이 사용자의 권한을 확인하는 과정을 인가(Authorization) 과정 이라고 부른답니다.
→ 사용자 인증 미들웨어는 인증 기능만 제공하고 있습니다. 이후 관리자 권한, 등급별 권한이 필요하다면, 별도의 인가(Authorization) 과정이 추가되어야 합니다.

 

middlewares 폴더를 생성하고, auth.middleware.js 파일을 생성하여 코드를 구현해보도록 하겠습니다.

[게시판 프로젝트] 사용자 인증 미들웨어

// src/middlewares/auth.middleware.js

import jwt from "jsonwebtoken";
import { prisma } from "../utils/prisma/index.js";

export default async function (req, res, next) {
    try {
        // 1 . 클라이언트로 부터 쿠키(Cookie)를 전달받습니다.
        const { authorization } = req.cookies;

        if (!authorization) throw new Error("토큰이 존재하지 않습니다.");

        const [tokenType, token] = authorization.split(" ");

        // 2 . 쿠키(Cookie)가 Bearer 토큰 형식인지 확인합니다.
        if (tokenType !== "Bearer") throw new Error("토큰 타입이 일치하지 않습니다.");

        // 3 . 서버에서 발급한 JWT가 맞는지 검증합니다. secret key와 비교
        const decodedToken = jwt.verify(token, "custom-secret-key");
        const userId = decodedToken.userId;

        // 4 . JWT의 userId를 이용해 사용자를 조회합니다.
        const user = await prisma.users.findFirst({
            where: { userId: +userId },
        });
        if (!user) {
            res.clearCookie("authorization");
            throw new Error("토큰 사용자가 존재하지 않습니다.");
        }

        // 5 . req.user 에 조회된 사용자 정보를 할당합니다.
        // req.user에 사용자 정보를 저장합니다.
        req.user = user;

        // 6 . 다음 미들웨어를 실행합니다.
        next();
    } catch (error) {
        res.clearCookie("authorization");

        // 토큰이 만료되었거나, 조작되었을 때, 에러 메시지를 다르게 출력합니다.
        switch (error.name) {
            case "TokenExpiredError":
                return res.status(401).json({ message: "토큰이 만료되었습니다." });
            case "JsonWebTokenError":
                return res.status(401).json({ message: "토큰이 조작되었습니다." });
            default:
                return res.status(401).json({ message: error.message ?? "비정상적인 요청입니다." });
        }
    }
}


7) [게시판 프로젝트] 사용자 정보 조회 API

사용자 정보 조회 API는 일반적으로 다른 사용자의 정보를 조회하기 위해 사용합니다.
예를들어 여러분들이 네이버 카페에 특정한 사용자의 정보를 조회하려고 프로필 상세보기를 클릭하였다면, 그때 사용자의 닉네임 뿐만아니라, 다양한 정보들을 조회할 수 있는 것 처럼 말이죠.

[게시판 프로젝트] 사용자 정보 조회 API 비즈니스 로직
1. 클라이언트가 로그인된 사용자인지 검증합니다.
2. 사용자를 조회할 때, 1:1 관계를 맺고 있는 Users와 UserInfos 테이블을 조회합니다.
3. 조회한 사용자의 상세한 정보클라이언트에게 반환합니다.


사용자 정보 조회 API는 단순히 Users 테이블 하나만 조회를 하는 것이아닌, UserInfos 테이블을 함께 조회합니다. 그렇기 때문에, 각각의 테이블을 1번씩 조회하게 되어 총 2번의 조회를 하게 되는 문제가 발생합니다. 이런 문제를 해결하기 위해 Prisma에서는 중첩 select라는 문법을 제공합니다.


[코드스니펫] [게시판 프로젝트] 사용자 정보 조회 API

// src/routes/users.route.js

import authMiddleware from "../middlewares/auth.middleware.js";

/** 사용자 조회 API **/
router.get('/users', authMiddleware, async (req, res, next) => {
  const { userId } = req.user;

  const user = await prisma.users.findFirst({
    where: { userId: +userId },
    select: {
      userId: true,
      email: true,
      createdAt: true,
      updatedAt: true,
      userInfos: {
        // 1:1 관계를 맺고있는 UserInfos 테이블을 조회합니다.
        select: {
          name: true,
          age: true,
          gender: true,
          profileImage: true,
        },
      },
    },
  });

  return res.status(200).json({ data: user });
});

test

insumnia의 manage Cookies

authorization=Bearer%20eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjIsImlhdCI6MTczMzA3NDY1Nn0.5Uak1c5lHvN6UGKJ3e_E8un84jcp4F-F6C1jI-PqJYE; Path=/; Domain=localhostEdit 

authorization=Bearer  쿠키는 Bearer토큰의 형식으로 jwt를 가지고 있습니다.  

로그인 이후에 API를 보낼 때 마다 Bearer형식을 가진 jwt로 전달이 되게 돼있습니다.

유저의 상세정보가 정상적으로 조회되었습니다.


❓ Prisma에서 연관 관계 테이블 조회하나요?
Prisma의 select 문법은 Query 메서드의 옵션으로 사용할 수 있습니다. 

await prisma.users.findFirst({
  where: { userId: +userId },
  select: {
    userId: true,
    email: true,
    createdAt: true,
    updatedAt: true,
    userInfos: {
      // 1:1 관계를 맺고있는 UserInfos 테이블을 조회합니다.
      select: {
        name: true,
        age: true,
        gender: true,
        profileImage: true,
      },
    },
  },
});


select 내에 또다른 select가 존재하는데, 이것을 중첩 select 문법이라고 부릅니다.

select 문 안에서 테이블의 중첩문은 테이블당 한번씩만 가능합니다.

여러번 호출시 에러 발생합니다.


중첩 select 는 SQL의 JOIN과 동일한 역할을 수행합니다.
중첩 select 문법을 사용하기 위해서는 Prisma model에서 @relation()과 같이 관계 설정이 되어야 합니다.
@relation()으로 Prisma는 현재 모델에서 참조하는 외래 키를 인식하고, SQL을 생성할 수 있게 됩니다.
만약, 현재 테이블과 연관된 테이블의 모든 컬럼을 조회하고 싶다면, include 문법으로도 조회할 수 있습니다!
→ 중첩 select 문법과 include 문법 : https://www.prisma.io/docs/orm/prisma-client/queries/relation-queries

 

// src/routes/users.router.js

더보기
// src/routes/users.router.js

import express from "express";
import bcrypt from "bcrypt";
import { prisma } from "../utils/prisma/index.js";
import jwt from "jsonwebtoken";
import authMiddleware from "../middlewares/auth.middleware.js";

const router = express.Router();

/** 사용자 회원가입 API 리팩토링**/
router.post("/sign-up", async (req, res, next) => {
    const { email, password, name, age, gender, profileImage } = req.body;
    const isExistUser = await prisma.users.findFirst({
        where: {
            email,
        },
    });

    if (isExistUser) {
        return res.status(409).json({ message: "이미 존재하는 이메일입니다." });
    }

    // 사용자 비밀번호를 암호화합니다.
    const hashedPassword = await bcrypt.hash(password, 10);

    // Users 테이블에 사용자를 추가합니다.
    const user = await prisma.users.create({
        data: {
            email,
            password: hashedPassword, // 암호화된 비밀번호를 저장합니다.
        },
    });

    // UserInfos 테이블에 사용자 정보를 추가합니다.
    const userInfo = await prisma.userInfos.create({
        data: {
            userId: user.userId, // 생성한 유저의 userId를 바탕으로 사용자 정보를 생성합니다.
            name,
            age,
            gender: gender.toUpperCase(), // 성별을 대문자로 변환합니다.
            profileImage,
        },
    });

    return res.status(201).json({ message: "회원가입이 완료되었습니다." });
});

/** 로그인 API **/
router.post("/sign-in", async (req, res, next) => {
    const { email, password } = req.body;
    // email로 user 찾기
    const user = await prisma.users.findFirst({ where: { email } });

    // 유저존재 확인 없으면 401 에러메시지전달
    if (!user) return res.status(401).json({ message: "존재하지 않는 이메일입니다." });
    // 입력받은 사용자의 비밀번호와 데이터베이스에 저장된 비밀번호를 비교합니다.
    // A와 B가 일치하지 않다 : !(await bcrypt.compare(A, B))
    else if (!(await bcrypt.compare(password, user.password))) return res.status(401).json({ message: "비밀번호가 일치하지 않습니다." });

    // 로그인에 성공하면, 사용자의 userId를 바탕으로 토큰을 생성합니다.
    const token = jwt.sign(
        {
            userId: user.userId,
        },
        // jwt가 해당하는 키를 정상적으로 확인하기 위해서 이 문장을 사용해야 합니다.
        "custom-secret-key" // secret key 설정
    );

    // 쿠키 할당
    // authotization 쿠키에 Bearer 토큰 형식으로 JWT를 저장합니다.
    res.cookie("authorization", `Bearer ${token}`);
    return res.status(200).json({ message: "로그인 성공" });
});

// [게시판 프로젝트] 사용자 정보 조회 API 비즈니스 로직
// 2. 사용자를 조회할 때, 1:1 관계를 맺고 있는 Users와 UserInfos 테이블을 조회합니다.
// 3. 조회한 사용자의 상세한 정보를 클라이언트에게 반환합니다.

/** 사용자 조회 API **/
router.get("/users", authMiddleware, async (req, res, next) => {
    const { userId } = req.user;

    // 1. 클라이언트가 로그인된 사용자인지 검증합니다.
    const user = await prisma.users.findFirst({
        where: { userId: +userId },
        select: {
            userId: true,
            email: true,
            createdAt: true,
            updatedAt: true,
            userInfos: {
                // 1:1 관계를 맺고있는 UserInfos 테이블을 조회합니다.
                select: {
                    name: true,
                    age: true,
                    gender: true,
                    profileImage: true,
                },
            },
        },
    });

    return res.status(200).json({ data: user });
});

export default router;